excalidraw/static/js/main.3def8d06.chunk.js.map

1 line
82 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{"version":3,"sources":["zindex.ts","index.tsx"],"names":["swap","elements","indexA","indexB","element","moveOneLeft","indicesToMove","sort","a","b","isSorted","forEach","index","i","moveOneRight","reversedIndicesToMove","length","moveAllLeft","leftMostElements","map","reverse","concat","pos","moveAllRight","rightMostElements","Array","of","skipHistory","stateHistory","generateHistoryCurrentEntry","JSON","stringify","isSelected","restoreHistoryEntry","entry","newElements","parse","splice","newElement","generateDraw","push","randomSeed","Math","floor","random","withCustomMathRandom","seed","cb","imul","LCG","result","distanceBetweenPointAndSegment","x","y","x1","y1","x2","y2","xx","yy","C","D","lenSquare","param","dx","dy","hypot","hitTest","type","px","abs","width","py","height","tx","ty","ex","ey","rx","ry","qx","qy","r","q","min","max","t","getElementAbsoluteX1","getElementAbsoluteX2","getElementAbsoluteY1","getElementAbsoluteY2","getArrowPoints","x3","y3","x4","y4","console","warn","Error","resizeTest","sceneState","handlers","handlerRectangles","filter","Object","keys","key","handler","scrollX","scrollY","getScrollbars","canvasWidth","canvasHeight","scrollBarWidth","horizontalScrollBar","SCROLLBAR_MARGIN","scrollBarHeight","horizontal","vertical","SCROLLBAR_WIDTH","elementX1","elementX2","elementY1","elementY2","marginX","marginY","renderScene","rc","canvas","offsetX","offsetY","renderScrollbars","renderSelection","context","getContext","fillStyle","viewBackgroundColor","fillRect","clearRect","selectedIndices","getSelectedIndices","draw","lineDash","getLineDash","setLineDash","strokeRect","margin","values","scrollBars","window","devicePixelRatio","saveFile","name","data","link","document","createElement","setAttribute","click","remove","rotate","angle","cos","sin","generator","rough","isTextElement","isInputLike","target","HTMLInputElement","HTMLTextAreaElement","HTMLSelectElement","distance","minSize","xs","ys","PI","shape","rectangle","stroke","strokeColor","fill","backgroundColor","translate","ellipse","shapes","line","font","fillText","text","actualBoundingBoxAscent","clearSelection","resetCursor","documentElement","style","cursor","deleteSelectedElements","restore","savedElements","savedState","e","KEYS","SHAPES","icon","viewBox","d","value","shapesShortcutKeys","findElementByKey","reduce","isArrowKey","keyCode","someElementIsSelected","some","lastCanvasWidth","lastCanvasHeight","lastMouseUp","App","state","draggingElement","resizingElement","elementType","exportBackground","currentItemStrokeColor","currentItemBackgroundColor","onResize","forceUpdate","onKeyDown","event","preventDefault","step","shiftKey","metaKey","altKey","code","includes","toLowerCase","setState","lastEntry","pop","undefined","clearCanvas","confirm","removeWheelEventListener","handleWheel","deltaX","deltaY","addEventListener","this","localStorage","getItem","removeEventListener","innerWidth","innerHeight","className","onCut","clipboardData","setData","onCopy","onPaste","parsedElements","paste","getData","isArray","parsedElement","checked","onChange","onClick","title","exportPadding","alert","subCanvasX1","Infinity","subCanvasX2","subCanvasY1","subCanvasY2","tempCanvas","display","body","appendChild","toDataURL","exportAsPNG","serialized","version","source","location","origin","encodeURIComponent","saveAsJSON","input","reader","FileReader","accept","onchange","files","readAsText","Promise","resolve","onloadend","readyState","DONE","loadFromJSON","then","id","ref","passive","scale","onMouseDown","button","activeElement","blur","clientX","clientY","resizeHandle","isDraggingElements","isResizingElements","resizeElement","find","hitElement","prompt","measureText","actualBoundingBoxDescent","lastX","lastY","onMouseMove","HTMLElement","el","selectedElements","selection","selectionX1","selectionX2","selectionY1","selectionY2","setSelection","onMouseUp","newEntry","setItem","React","Component","rootElement","getElementById","ReactDOM","render"],"mappings":"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","file":"static/js/main.3def8d06.chunk.js","sourcesContent":["function swap<T>(elements: T[], indexA: number, indexB: number) {\n const element = elements[indexA];\n elements[indexA] = elements[indexB];\n elements[indexB] = element;\n}\n\nexport function moveOneLeft<T>(elements: T[], indicesToMove: number[]) {\n indicesToMove.sort((a: number, b: number) => a - b);\n let isSorted = true;\n // We go from left to right to avoid overriding the wrong elements\n indicesToMove.forEach((index, i) => {\n // We don't want to bubble the first elements that are sorted as they are\n // already in their correct position\n isSorted = isSorted && index === i;\n if (isSorted) {\n return;\n }\n swap(elements, index - 1, index);\n });\n}\n\nexport function moveOneRight<T>(elements: T[], indicesToMove: number[]) {\n const reversedIndicesToMove = indicesToMove.sort(\n (a: number, b: number) => b - a\n );\n let isSorted = true;\n\n // We go from right to left to avoid overriding the wrong elements\n reversedIndicesToMove.forEach((index, i) => {\n // We don't want to bubble the first elements that are sorted as they are\n // already in their correct position\n isSorted = isSorted && index === elements.length - i - 1;\n if (isSorted) {\n return;\n }\n swap(elements, index + 1, index);\n });\n}\n\n// Let's go through an example\n// | |\n// [a, b, c, d, e, f, g]\n// -->\n// [c, f, a, b, d, e, g]\n//\n// We are going to override all the elements we want to move, so we keep them in an array\n// that we will restore at the end.\n// [c, f]\n//\n// From now on, we'll never read those values from the array anymore\n// |1 |0\n// [a, b, _, d, e, _, g]\n//\n// The idea is that we want to shift all the elements between the marker 0 and 1\n// by one slot to the right.\n//\n// |1 |0\n// [a, b, _, d, e, _, g]\n// -> ->\n//\n// which gives us\n//\n// |1 |0\n// [a, b, _, _, d, e, g]\n//\n// Now, we need to move all the elements from marker 1 to the beginning by two (not one)\n// slots to the right, which gives us\n//\n// |1 |0\n// [a, b, _, _, d, e, g]\n// ---|--^ ^\n// ------|\n//\n// which gives us\n//\n// |1 |0\n// [_, _, a, b, d, e, g]\n//\n// At this point, we can fill back the leftmost elements with the array we saved at\n// the beggining\n//\n// |1 |0\n// [c, f, a, b, d, e, g]\n//\n// And we are done!\nexport function moveAllLeft<T>(elements: T[], indicesToMove: number[]) {\n indicesToMove.sort((a: number, b: number) => a - b);\n\n // Copy the elements to move\n const leftMostElements = indicesToMove.map(index => elements[index]);\n\n const reversedIndicesToMove = indicesToMove\n // We go from right to left to avoid overriding elements.\n .reverse()\n // We add 0 for the final marker\n .concat([0]);\n\n reversedIndicesToMove.forEach((index, i) => {\n // We skip the first one as it is not paired with anything else\n if (i === 0) {\n return;\n }\n\n // We go from the next marker to the right (i - 1) to the current one (index)\n for (let pos = reversedIndicesToMove[i - 1] - 1; pos >= index; --pos) {\n // We move by 1 the first time, 2 the second... So we can use the index i in the array\n elements[pos + i] = elements[pos];\n }\n });\n\n // The final step\n leftMostElements.forEach((element, i) => {\n elements[i] = element;\n });\n}\n\n// Let's go through an example\n// | |\n// [a, b, c, d, e, f, g]\n// -->\n// [a, b, d, e, g, c, f]\n//\n// We are going to override all the elements we want to move, so we keep them in an array\n// that we will restore at the end.\n// [c, f]\n//\n// From now on, we'll never read those values from the array anymore\n// |0 |1\n// [a, b, _, d, e, _, g]\n//\n// The idea is that we want to shift all the elements between the marker 0 and 1\n// by one slot to the left.\n//\n// |0 |1\n// [a, b, _, d, e, _, g]\n// <- <-\n//\n// which gives us\n//\n// |0 |1\n// [a, b, d, e, _, _, g]\n//\n// Now, we need to move all the elements from marker 1 to the end by two (not one)\n// slots to the left, which gives us\n//\n// |0 |1\n// [a, b, d, e, _, _, g]\n// ^------\n//\n// which gives us\n//\n// |0 |1\n// [a, b, d, e, g, _, _]\n//\n// At this point, we can fill back the rightmost elements with the array we saved at\n// the beggining\n//\n// |0 |1\n// [a, b, d, e, g, c, f]\n//\n// And we are done!\nexport function moveAllRight<T>(elements: T[], indicesToMove: number[]) {\n const reversedIndicesToMove = indicesToMove.sort(\n (a: number, b: number) => b - a\n );\n\n // Copy the elements to move\n const rightMostElements = reversedIndicesToMove.map(index => elements[index]);\n\n indicesToMove = reversedIndicesToMove\n // We go from left to right to avoid overriding elements.\n .reverse()\n // We last element index for the final marker\n .concat([elements.length]);\n\n indicesToMove.forEach((index, i) => {\n // We skip the first one as it is not paired with anything else\n if (i === 0) {\n return;\n }\n\n // We go from the next marker to the left (i - 1) to the current one (index)\n for (let pos = indicesToMove[i - 1] + 1; pos < index; ++pos) {\n // We move by 1 the first time, 2 the second... So we can use the index i in the array\n elements[pos - i] = elements[pos];\n }\n });\n\n // The final step\n rightMostElements.forEach((element, i) => {\n elements[elements.length - i - 1] = element;\n });\n}\n","import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport rough from \"roughjs/bin/wrappers/rough\";\nimport { RoughCanvas } from \"roughjs/bin/canvas\";\n\nimport { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from \"./zindex\";\n\nimport \"./styles.scss\";\n\ntype ExcalidrawElement = ReturnType<typeof newElement>;\ntype ExcalidrawTextElement = ExcalidrawElement & {\n type: \"text\";\n font: string;\n text: string;\n actualBoundingBoxAscent: number;\n};\n\nconst LOCAL_STORAGE_KEY = \"excalidraw\";\nconst LOCAL_STORAGE_KEY_STATE = \"excalidraw-state\";\n\nconst elements = Array.of<ExcalidrawElement>();\n\nlet skipHistory = false;\nconst stateHistory: string[] = [];\nfunction generateHistoryCurrentEntry() {\n return JSON.stringify(\n elements.map(element => ({ ...element, isSelected: false }))\n );\n}\nfunction pushHistoryEntry(newEntry: string) {\n if (\n stateHistory.length > 0 &&\n stateHistory[stateHistory.length - 1] === newEntry\n ) {\n // If the last entry is the same as this one, ignore it\n return;\n }\n stateHistory.push(newEntry);\n}\nfunction restoreHistoryEntry(entry: string) {\n const newElements = JSON.parse(entry);\n elements.splice(0, elements.length);\n newElements.forEach((newElement: ExcalidrawElement) => {\n generateDraw(newElement);\n elements.push(newElement);\n });\n // When restoring, we shouldn't add an history entry otherwise we'll be stuck with it and can't go back\n skipHistory = true;\n}\n\n// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript/47593316#47593316\nconst LCG = (seed: number) => () =>\n ((2 ** 31 - 1) & (seed = Math.imul(48271, seed))) / 2 ** 31;\n\nfunction randomSeed() {\n return Math.floor(Math.random() * 2 ** 31);\n}\n\n// Unfortunately, roughjs doesn't support a seed attribute (https://github.com/pshihn/rough/issues/27).\n// We can achieve the same result by overriding the Math.random function with a\n// pseudo random generator that supports a random seed and swapping it back after.\nfunction withCustomMathRandom<T>(seed: number, cb: () => T): T {\n const random = Math.random;\n Math.random = LCG(seed);\n const result = cb();\n Math.random = random;\n return result;\n}\n\n// https://stackoverflow.com/a/6853926/232122\nfunction distanceBetweenPointAndSegment(\n x: number,\n y: number,\n x1: number,\n y1: number,\n x2: number,\n y2: number\n) {\n const A = x - x1;\n const B = y - y1;\n const C = x2 - x1;\n const D = y2 - y1;\n\n const dot = A * C + B * D;\n const lenSquare = C * C + D * D;\n let param = -1;\n if (lenSquare !== 0) {\n // in case of 0 length line\n param = dot / lenSquare;\n }\n\n let xx, yy;\n if (param < 0) {\n xx = x1;\n yy = y1;\n } else if (param > 1) {\n xx = x2;\n yy = y2;\n } else {\n xx = x1 + param * C;\n yy = y1 + param * D;\n }\n\n const dx = x - xx;\n const dy = y - yy;\n return Math.hypot(dx, dy);\n}\n\nfunction hitTest(element: ExcalidrawElement, x: number, y: number): boolean {\n // For shapes that are composed of lines, we only enable point-selection when the distance\n // of the click is less than x pixels of any of the lines that the shape is composed of\n const lineThreshold = 10;\n\n if (element.type === \"ellipse\") {\n // https://stackoverflow.com/a/46007540/232122\n const px = Math.abs(x - element.x - element.width / 2);\n const py = Math.abs(y - element.y - element.height / 2);\n\n let tx = 0.707;\n let ty = 0.707;\n\n const a = element.width / 2;\n const b = element.height / 2;\n\n [0, 1, 2, 3].forEach(x => {\n const xx = a * tx;\n const yy = b * ty;\n\n const ex = ((a * a - b * b) * tx ** 3) / a;\n const ey = ((b * b - a * a) * ty ** 3) / b;\n\n const rx = xx - ex;\n const ry = yy - ey;\n\n const qx = px - ex;\n const qy = py - ey;\n\n const r = Math.hypot(ry, rx);\n const q = Math.hypot(qy, qx);\n\n tx = Math.min(1, Math.max(0, ((qx * r) / q + ex) / a));\n ty = Math.min(1, Math.max(0, ((qy * r) / q + ey) / b));\n const t = Math.hypot(ty, tx);\n tx /= t;\n ty /= t;\n });\n\n return Math.hypot(a * tx - px, b * ty - py) < lineThreshold;\n } else if (element.type === \"rectangle\") {\n const x1 = getElementAbsoluteX1(element);\n const x2 = getElementAbsoluteX2(element);\n const y1 = getElementAbsoluteY1(element);\n const y2 = getElementAbsoluteY2(element);\n\n // (x1, y1) --A-- (x2, y1)\n // |D |B\n // (x1, y2) --C-- (x2, y2)\n return (\n distanceBetweenPointAndSegment(x, y, x1, y1, x2, y1) < lineThreshold || // A\n distanceBetweenPointAndSegment(x, y, x2, y1, x2, y2) < lineThreshold || // B\n distanceBetweenPointAndSegment(x, y, x2, y2, x1, y2) < lineThreshold || // C\n distanceBetweenPointAndSegment(x, y, x1, y2, x1, y1) < lineThreshold // D\n );\n } else if (element.type === \"arrow\") {\n let [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);\n // The computation is done at the origin, we need to add a translation\n x -= element.x;\n y -= element.y;\n\n return (\n // \\\n distanceBetweenPointAndSegment(x, y, x3, y3, x2, y2) < lineThreshold ||\n // -----\n distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) < lineThreshold ||\n // /\n distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold\n );\n } else if (element.type === \"text\") {\n const x1 = getElementAbsoluteX1(element);\n const x2 = getElementAbsoluteX2(element);\n const y1 = getElementAbsoluteY1(element);\n const y2 = getElementAbsoluteY2(element);\n\n return x >= x1 && x <= x2 && y >= y1 && y <= y2;\n } else if (element.type === \"selection\") {\n console.warn(\"This should not happen, we need to investigate why it does.\");\n return false;\n } else {\n throw new Error(\"Unimplemented type \" + element.type);\n }\n}\n\nfunction resizeTest(\n element: ExcalidrawElement,\n x: number,\n y: number,\n sceneState: SceneState\n): string | false {\n if (element.type === \"text\" || element.type === \"arrow\") return false;\n\n const handlers = handlerRectangles(element, sceneState);\n\n const filter = Object.keys(handlers).filter(key => {\n const handler = handlers[key];\n\n return (\n x + sceneState.scrollX >= handler[0] &&\n x + sceneState.scrollX <= handler[0] + handler[2] &&\n y + sceneState.scrollY >= handler[1] &&\n y + sceneState.scrollY <= handler[1] + handler[3]\n );\n });\n\n if (filter.length > 0) {\n return filter[0];\n }\n\n return false;\n}\n\nfunction newElement(\n type: string,\n x: number,\n y: number,\n strokeColor: string,\n backgroundColor: string,\n width = 0,\n height = 0\n) {\n const element = {\n type: type,\n x: x,\n y: y,\n width: width,\n height: height,\n isSelected: false,\n strokeColor: strokeColor,\n backgroundColor: backgroundColor,\n seed: randomSeed(),\n draw(\n rc: RoughCanvas,\n context: CanvasRenderingContext2D,\n sceneState: SceneState\n ) {}\n };\n return element;\n}\n\ntype SceneState = {\n scrollX: number;\n scrollY: number;\n // null indicates transparent bg\n viewBackgroundColor: string | null;\n};\n\nconst SCROLLBAR_WIDTH = 6;\nconst SCROLLBAR_MARGIN = 4;\nconst SCROLLBAR_COLOR = \"rgba(0,0,0,0.3)\";\nconst CANVAS_WINDOW_OFFSET_LEFT = 250;\nconst CANVAS_WINDOW_OFFSET_TOP = 0;\n\nfunction getScrollbars(\n canvasWidth: number,\n canvasHeight: number,\n scrollX: number,\n scrollY: number\n) {\n // horizontal scrollbar\n const sceneWidth = canvasWidth + Math.abs(scrollX);\n const scrollBarWidth = (canvasWidth * canvasWidth) / sceneWidth;\n const scrollBarX = scrollX > 0 ? 0 : canvasWidth - scrollBarWidth;\n const horizontalScrollBar = {\n x: scrollBarX + SCROLLBAR_MARGIN,\n y: canvasHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,\n width: scrollBarWidth - SCROLLBAR_MARGIN * 2,\n height: SCROLLBAR_WIDTH\n };\n\n // vertical scrollbar\n const sceneHeight = canvasHeight + Math.abs(scrollY);\n const scrollBarHeight = (canvasHeight * canvasHeight) / sceneHeight;\n const scrollBarY = scrollY > 0 ? 0 : canvasHeight - scrollBarHeight;\n const verticalScrollBar = {\n x: canvasWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,\n y: scrollBarY + SCROLLBAR_MARGIN,\n width: SCROLLBAR_WIDTH,\n height: scrollBarHeight - SCROLLBAR_WIDTH * 2\n };\n\n return {\n horizontal: horizontalScrollBar,\n vertical: verticalScrollBar\n };\n}\n\nfunction handlerRectangles(element: ExcalidrawElement, sceneState: SceneState) {\n const elementX1 = element.x;\n const elementX2 = element.x + element.width;\n const elementY1 = element.y;\n const elementY2 = element.y + element.height;\n\n const margin = 4;\n const minimumSize = 40;\n const handlers: { [handler: string]: number[] } = {};\n\n const marginX = element.width < 0 ? 8 : -8;\n const marginY = element.height < 0 ? 8 : -8;\n\n if (Math.abs(elementX2 - elementX1) > minimumSize) {\n handlers[\"n\"] = [\n elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4,\n elementY1 - margin + sceneState.scrollY + marginY,\n 8,\n 8\n ];\n\n handlers[\"s\"] = [\n elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4,\n elementY2 - margin + sceneState.scrollY - marginY,\n 8,\n 8\n ];\n }\n\n if (Math.abs(elementY2 - elementY1) > minimumSize) {\n handlers[\"w\"] = [\n elementX1 - margin + sceneState.scrollX + marginX,\n elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4,\n 8,\n 8\n ];\n\n handlers[\"e\"] = [\n elementX2 - margin + sceneState.scrollX - marginX,\n elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4,\n 8,\n 8\n ];\n }\n\n handlers[\"nw\"] = [\n elementX1 - margin + sceneState.scrollX + marginX,\n elementY1 - margin + sceneState.scrollY + marginY,\n 8,\n 8\n ]; // nw\n handlers[\"ne\"] = [\n elementX2 - margin + sceneState.scrollX - marginX,\n elementY1 - margin + sceneState.scrollY + marginY,\n 8,\n 8\n ]; // ne\n handlers[\"sw\"] = [\n elementX1 - margin + sceneState.scrollX + marginX,\n elementY2 - margin + sceneState.scrollY - marginY,\n 8,\n 8\n ]; // sw\n handlers[\"se\"] = [\n elementX2 - margin + sceneState.scrollX - marginX,\n elementY2 - margin + sceneState.scrollY - marginY,\n 8,\n 8\n ]; // se\n\n return handlers;\n}\n\nfunction renderScene(\n rc: RoughCanvas,\n canvas: HTMLCanvasElement,\n sceneState: SceneState,\n // extra options, currently passed by export helper\n {\n offsetX,\n offsetY,\n renderScrollbars = true,\n renderSelection = true\n }: {\n offsetX?: number;\n offsetY?: number;\n renderScrollbars?: boolean;\n renderSelection?: boolean;\n } = {}\n) {\n if (!canvas) return;\n const context = canvas.getContext(\"2d\")!;\n\n const fillStyle = context.fillStyle;\n if (typeof sceneState.viewBackgroundColor === \"string\") {\n context.fillStyle = sceneState.viewBackgroundColor;\n context.fillRect(0, 0, canvas.width, canvas.height);\n } else {\n context.clearRect(0, 0, canvas.width, canvas.height);\n }\n context.fillStyle = fillStyle;\n\n const selectedIndices = getSelectedIndices();\n\n sceneState = {\n ...sceneState,\n scrollX: typeof offsetX === \"number\" ? offsetX : sceneState.scrollX,\n scrollY: typeof offsetY === \"number\" ? offsetY : sceneState.scrollY\n };\n\n elements.forEach(element => {\n element.draw(rc, context, sceneState);\n if (renderSelection && element.isSelected) {\n const margin = 4;\n\n const elementX1 = getElementAbsoluteX1(element);\n const elementX2 = getElementAbsoluteX2(element);\n const elementY1 = getElementAbsoluteY1(element);\n const elementY2 = getElementAbsoluteY2(element);\n const lineDash = context.getLineDash();\n context.setLineDash([8, 4]);\n context.strokeRect(\n elementX1 - margin + sceneState.scrollX,\n elementY1 - margin + sceneState.scrollY,\n elementX2 - elementX1 + margin * 2,\n elementY2 - elementY1 + margin * 2\n );\n context.setLineDash(lineDash);\n\n if (\n element.type !== \"text\" &&\n element.type !== \"arrow\" &&\n selectedIndices.length === 1\n ) {\n const handlers = handlerRectangles(element, sceneState);\n Object.values(handlers).forEach(handler => {\n context.strokeRect(handler[0], handler[1], handler[2], handler[3]);\n });\n }\n }\n });\n\n if (renderScrollbars) {\n const scrollBars = getScrollbars(\n context.canvas.width / window.devicePixelRatio,\n context.canvas.height / window.devicePixelRatio,\n sceneState.scrollX,\n sceneState.scrollY\n );\n\n context.fillStyle = SCROLLBAR_COLOR;\n context.fillRect(\n scrollBars.horizontal.x,\n scrollBars.horizontal.y,\n scrollBars.horizontal.width,\n scrollBars.horizontal.height\n );\n context.fillRect(\n scrollBars.vertical.x,\n scrollBars.vertical.y,\n scrollBars.vertical.width,\n scrollBars.vertical.height\n );\n context.fillStyle = fillStyle;\n }\n}\n\nfunction saveAsJSON() {\n const serialized = JSON.stringify({\n version: 1,\n source: window.location.origin,\n elements\n });\n\n saveFile(\n \"excalidraw.json\",\n \"data:text/plain;charset=utf-8,\" + encodeURIComponent(serialized)\n );\n}\n\nfunction loadFromJSON() {\n const input = document.createElement(\"input\");\n const reader = new FileReader();\n input.type = \"file\";\n input.accept = \".json\";\n\n input.onchange = () => {\n if (!input.files!.length) {\n alert(\"A file was not selected.\");\n return;\n }\n\n reader.readAsText(input.files![0], \"utf8\");\n };\n\n input.click();\n\n return new Promise(resolve => {\n reader.onloadend = () => {\n if (reader.readyState === FileReader.DONE) {\n const data = JSON.parse(reader.result as string);\n restore(data.elements, null);\n resolve();\n }\n };\n });\n}\n\nfunction exportAsPNG({\n exportBackground,\n exportPadding = 10,\n viewBackgroundColor\n}: {\n exportBackground: boolean;\n exportPadding?: number;\n viewBackgroundColor: string;\n scrollX: number;\n scrollY: number;\n}) {\n if (!elements.length) return window.alert(\"Cannot export empty canvas.\");\n\n // calculate smallest area to fit the contents in\n\n let subCanvasX1 = Infinity;\n let subCanvasX2 = 0;\n let subCanvasY1 = Infinity;\n let subCanvasY2 = 0;\n\n elements.forEach(element => {\n subCanvasX1 = Math.min(subCanvasX1, getElementAbsoluteX1(element));\n subCanvasX2 = Math.max(subCanvasX2, getElementAbsoluteX2(element));\n subCanvasY1 = Math.min(subCanvasY1, getElementAbsoluteY1(element));\n subCanvasY2 = Math.max(subCanvasY2, getElementAbsoluteY2(element));\n });\n\n function distance(x: number, y: number) {\n return Math.abs(x > y ? x - y : y - x);\n }\n\n const tempCanvas = document.createElement(\"canvas\");\n tempCanvas.style.display = \"none\";\n document.body.appendChild(tempCanvas);\n tempCanvas.width = distance(subCanvasX1, subCanvasX2) + exportPadding * 2;\n tempCanvas.height = distance(subCanvasY1, subCanvasY2) + exportPadding * 2;\n\n renderScene(\n rough.canvas(tempCanvas),\n tempCanvas,\n {\n viewBackgroundColor: exportBackground ? viewBackgroundColor : null,\n scrollX: 0,\n scrollY: 0\n },\n {\n offsetX: -subCanvasX1 + exportPadding,\n offsetY: -subCanvasY1 + exportPadding,\n renderScrollbars: false,\n renderSelection: false\n }\n );\n\n saveFile(\"excalidraw.png\", tempCanvas.toDataURL(\"image/png\"));\n\n // clean up the DOM\n if (tempCanvas !== canvas) tempCanvas.remove();\n}\n\nfunction saveFile(name: string, data: string) {\n // create a temporary <a> elem which we'll use to download the image\n const link = document.createElement(\"a\");\n link.setAttribute(\"download\", name);\n link.setAttribute(\"href\", data);\n link.click();\n\n // clean up\n link.remove();\n}\n\nfunction rotate(x1: number, y1: number, x2: number, y2: number, angle: number) {\n // 𝑎𝑥=(𝑎𝑥𝑐𝑥)cos𝜃(𝑎𝑦𝑐𝑦)sin𝜃+𝑐𝑥\n // 𝑎𝑦=(𝑎𝑥𝑐𝑥)sin𝜃+(𝑎𝑦𝑐𝑦)cos𝜃+𝑐𝑦.\n // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line\n return [\n (x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,\n (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2\n ];\n}\n\n// Casting second argument (DrawingSurface) to any,\n// because it is requred by TS definitions and not required at runtime\nconst generator = rough.generator(null, null as any);\n\nfunction isTextElement(\n element: ExcalidrawElement\n): element is ExcalidrawTextElement {\n return element.type === \"text\";\n}\n\nfunction isInputLike(\n target: Element | EventTarget | null\n): target is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement {\n return (\n target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n target instanceof HTMLSelectElement\n );\n}\n\nfunction getArrowPoints(element: ExcalidrawElement) {\n const x1 = 0;\n const y1 = 0;\n const x2 = element.width;\n const y2 = element.height;\n\n const size = 30; // pixels\n const distance = Math.hypot(x2 - x1, y2 - y1);\n // Scale down the arrow until we hit a certain size so that it doesn't look weird\n const minSize = Math.min(size, distance / 2);\n const xs = x2 - ((x2 - x1) / distance) * minSize;\n const ys = y2 - ((y2 - y1) / distance) * minSize;\n\n const angle = 20; // degrees\n const [x3, y3] = rotate(xs, ys, x2, y2, (-angle * Math.PI) / 180);\n const [x4, y4] = rotate(xs, ys, x2, y2, (angle * Math.PI) / 180);\n\n return [x1, y1, x2, y2, x3, y3, x4, y4];\n}\n\nfunction generateDraw(element: ExcalidrawElement) {\n if (element.type === \"selection\") {\n element.draw = (rc, context, { scrollX, scrollY }) => {\n const fillStyle = context.fillStyle;\n context.fillStyle = \"rgba(0, 0, 255, 0.10)\";\n context.fillRect(\n element.x + scrollX,\n element.y + scrollY,\n element.width,\n element.height\n );\n context.fillStyle = fillStyle;\n };\n } else if (element.type === \"rectangle\") {\n const shape = withCustomMathRandom(element.seed, () => {\n return generator.rectangle(0, 0, element.width, element.height, {\n stroke: element.strokeColor,\n fill: element.backgroundColor\n });\n });\n element.draw = (rc, context, { scrollX, scrollY }) => {\n context.translate(element.x + scrollX, element.y + scrollY);\n rc.draw(shape);\n context.translate(-element.x - scrollX, -element.y - scrollY);\n };\n } else if (element.type === \"ellipse\") {\n const shape = withCustomMathRandom(element.seed, () =>\n generator.ellipse(\n element.width / 2,\n element.height / 2,\n element.width,\n element.height,\n { stroke: element.strokeColor, fill: element.backgroundColor }\n )\n );\n element.draw = (rc, context, { scrollX, scrollY }) => {\n context.translate(element.x + scrollX, element.y + scrollY);\n rc.draw(shape);\n context.translate(-element.x - scrollX, -element.y - scrollY);\n };\n } else if (element.type === \"arrow\") {\n const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);\n const shapes = withCustomMathRandom(element.seed, () => [\n // \\\n generator.line(x3, y3, x2, y2, { stroke: element.strokeColor }),\n // -----\n generator.line(x1, y1, x2, y2, { stroke: element.strokeColor }),\n // /\n generator.line(x4, y4, x2, y2, { stroke: element.strokeColor })\n ]);\n\n element.draw = (rc, context, { scrollX, scrollY }) => {\n context.translate(element.x + scrollX, element.y + scrollY);\n shapes.forEach(shape => rc.draw(shape));\n context.translate(-element.x - scrollX, -element.y - scrollY);\n };\n return;\n } else if (isTextElement(element)) {\n element.draw = (rc, context, { scrollX, scrollY }) => {\n const font = context.font;\n context.font = element.font;\n const fillStyle = context.fillStyle;\n context.fillStyle = element.strokeColor;\n context.fillText(\n element.text,\n element.x + scrollX,\n element.y + element.actualBoundingBoxAscent + scrollY\n );\n context.fillStyle = fillStyle;\n context.font = font;\n };\n } else {\n throw new Error(\"Unimplemented type \" + element.type);\n }\n}\n\n// If the element is created from right to left, the width is going to be negative\n// This set of functions retrieves the absolute position of the 4 points.\n// We can't just always normalize it since we need to remember the fact that an arrow\n// is pointing left or right.\nfunction getElementAbsoluteX1(element: ExcalidrawElement) {\n return element.width >= 0 ? element.x : element.x + element.width;\n}\nfunction getElementAbsoluteX2(element: ExcalidrawElement) {\n return element.width >= 0 ? element.x + element.width : element.x;\n}\nfunction getElementAbsoluteY1(element: ExcalidrawElement) {\n return element.height >= 0 ? element.y : element.y + element.height;\n}\nfunction getElementAbsoluteY2(element: ExcalidrawElement) {\n return element.height >= 0 ? element.y + element.height : element.y;\n}\n\nfunction setSelection(selection: ExcalidrawElement) {\n const selectionX1 = getElementAbsoluteX1(selection);\n const selectionX2 = getElementAbsoluteX2(selection);\n const selectionY1 = getElementAbsoluteY1(selection);\n const selectionY2 = getElementAbsoluteY2(selection);\n elements.forEach(element => {\n const elementX1 = getElementAbsoluteX1(element);\n const elementX2 = getElementAbsoluteX2(element);\n const elementY1 = getElementAbsoluteY1(element);\n const elementY2 = getElementAbsoluteY2(element);\n element.isSelected =\n element.type !== \"selection\" &&\n selectionX1 <= elementX1 &&\n selectionY1 <= elementY1 &&\n selectionX2 >= elementX2 &&\n selectionY2 >= elementY2;\n });\n}\n\nfunction clearSelection() {\n elements.forEach(element => {\n element.isSelected = false;\n });\n}\n\nfunction resetCursor() {\n document.documentElement.style.cursor = \"\";\n}\n\nfunction deleteSelectedElements() {\n for (let i = elements.length - 1; i >= 0; --i) {\n if (elements[i].isSelected) {\n elements.splice(i, 1);\n }\n }\n}\n\nfunction save(state: AppState) {\n localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(elements));\n localStorage.setItem(LOCAL_STORAGE_KEY_STATE, JSON.stringify(state));\n}\n\nfunction restoreFromLocalStorage() {\n const savedElements = localStorage.getItem(LOCAL_STORAGE_KEY);\n const savedState = localStorage.getItem(LOCAL_STORAGE_KEY_STATE);\n\n return restore(savedElements, savedState);\n}\n\nfunction restore(\n savedElements: string | ExcalidrawElement[] | null,\n savedState: string | null\n) {\n try {\n if (savedElements) {\n elements.splice(\n 0,\n elements.length,\n ...(typeof savedElements === \"string\"\n ? JSON.parse(savedElements)\n : savedElements)\n );\n elements.forEach((element: ExcalidrawElement) => generateDraw(element));\n }\n\n return savedState ? JSON.parse(savedState) : null;\n } catch (e) {\n elements.splice(0, elements.length);\n return null;\n }\n}\n\ntype AppState = {\n draggingElement: ExcalidrawElement | null;\n resizingElement: ExcalidrawElement | null;\n elementType: string;\n exportBackground: boolean;\n currentItemStrokeColor: string;\n currentItemBackgroundColor: string;\n viewBackgroundColor: string;\n scrollX: number;\n scrollY: number;\n};\n\nconst KEYS = {\n ARROW_LEFT: \"ArrowLeft\",\n ARROW_RIGHT: \"ArrowRight\",\n ARROW_DOWN: \"ArrowDown\",\n ARROW_UP: \"ArrowUp\",\n ESCAPE: \"Escape\",\n DELETE: \"Delete\",\n BACKSPACE: \"Backspace\"\n};\n\n// We inline font-awesome icons in order to save on js size rather than including the font awesome react library\nconst SHAPES = [\n {\n icon: (\n // fa-mouse-pointer\n <svg viewBox=\"0 0 320 512\">\n <path d=\"M302.189 329.126H196.105l55.831 135.993c3.889 9.428-.555 19.999-9.444 23.999l-49.165 21.427c-9.165 4-19.443-.571-23.332-9.714l-53.053-129.136-86.664 89.138C18.729 472.71 0 463.554 0 447.977V18.299C0 1.899 19.921-6.096 30.277 5.443l284.412 292.542c11.472 11.179 3.007 31.141-12.5 31.141z\" />\n </svg>\n ),\n value: \"selection\"\n },\n {\n icon: (\n // fa-square\n <svg viewBox=\"0 0 448 512\">\n <path d=\"M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z\" />\n </svg>\n ),\n value: \"rectangle\"\n },\n {\n icon: (\n // fa-circle\n <svg viewBox=\"0 0 512 512\">\n <path d=\"M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z\" />\n </svg>\n ),\n value: \"ellipse\"\n },\n {\n icon: (\n // fa-long-arrow-alt-right\n <svg viewBox=\"0 0 448 512\">\n <path d=\"M313.941 216H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12h301.941v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.569 0-33.941l-86.059-86.059c-15.119-15.119-40.971-4.411-40.971 16.971V216z\" />\n </svg>\n ),\n value: \"arrow\"\n },\n {\n icon: (\n // fa-font\n <svg viewBox=\"0 0 448 512\">\n <path d=\"M432 416h-23.41L277.88 53.69A32 32 0 0 0 247.58 32h-47.16a32 32 0 0 0-30.3 21.69L39.41 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16h-19.58l23.3-64h152.56l23.3 64H304a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM176.85 272L224 142.51 271.15 272z\" />\n </svg>\n ),\n value: \"text\"\n }\n];\n\nconst shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);\n\nfunction findElementByKey(key: string) {\n const defaultElement = \"selection\";\n return SHAPES.reduce((element, shape) => {\n if (shape.value[0] !== key) return element;\n\n return shape.value;\n }, defaultElement);\n}\n\nfunction isArrowKey(keyCode: string) {\n return (\n keyCode === KEYS.ARROW_LEFT ||\n keyCode === KEYS.ARROW_RIGHT ||\n keyCode === KEYS.ARROW_DOWN ||\n keyCode === KEYS.ARROW_UP\n );\n}\n\nfunction getSelectedIndices() {\n const selectedIndices: number[] = [];\n elements.forEach((element, index) => {\n if (element.isSelected) {\n selectedIndices.push(index);\n }\n });\n return selectedIndices;\n}\n\nconst someElementIsSelected = () =>\n elements.some(element => element.isSelected);\n\nconst ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;\nconst ELEMENT_TRANSLATE_AMOUNT = 1;\n\nlet lastCanvasWidth = -1;\nlet lastCanvasHeight = -1;\n\nlet lastMouseUp: ((e: any) => void) | null = null;\n\nclass App extends React.Component<{}, AppState> {\n public componentDidMount() {\n document.addEventListener(\"keydown\", this.onKeyDown, false);\n window.addEventListener(\"resize\", this.onResize, false);\n\n const savedState = restoreFromLocalStorage();\n if (savedState) {\n this.setState(savedState);\n }\n }\n\n public componentWillUnmount() {\n document.removeEventListener(\"keydown\", this.onKeyDown, false);\n window.removeEventListener(\"resize\", this.onResize, false);\n }\n\n public state: AppState = {\n draggingElement: null,\n resizingElement: null,\n elementType: \"selection\",\n exportBackground: true,\n currentItemStrokeColor: \"#000000\",\n currentItemBackgroundColor: \"#ffffff\",\n viewBackgroundColor: \"#ffffff\",\n scrollX: 0,\n scrollY: 0\n };\n\n private onResize = () => {\n this.forceUpdate();\n };\n\n private onKeyDown = (event: KeyboardEvent) => {\n if (isInputLike(event.target)) return;\n\n if (event.key === KEYS.ESCAPE) {\n clearSelection();\n this.forceUpdate();\n event.preventDefault();\n } else if (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE) {\n deleteSelectedElements();\n this.forceUpdate();\n event.preventDefault();\n } else if (isArrowKey(event.key)) {\n const step = event.shiftKey\n ? ELEMENT_SHIFT_TRANSLATE_AMOUNT\n : ELEMENT_TRANSLATE_AMOUNT;\n elements.forEach(element => {\n if (element.isSelected) {\n if (event.key === KEYS.ARROW_LEFT) element.x -= step;\n else if (event.key === KEYS.ARROW_RIGHT) element.x += step;\n else if (event.key === KEYS.ARROW_UP) element.y -= step;\n else if (event.key === KEYS.ARROW_DOWN) element.y += step;\n }\n });\n this.forceUpdate();\n event.preventDefault();\n\n // Send backward: Cmd-Shift-Alt-B\n } else if (\n event.metaKey &&\n event.shiftKey &&\n event.altKey &&\n event.code === \"KeyB\"\n ) {\n this.moveOneLeft();\n event.preventDefault();\n\n // Send to back: Cmd-Shift-B\n } else if (event.metaKey && event.shiftKey && event.code === \"KeyB\") {\n this.moveAllLeft();\n event.preventDefault();\n\n // Bring forward: Cmd-Shift-Alt-F\n } else if (\n event.metaKey &&\n event.shiftKey &&\n event.altKey &&\n event.code === \"KeyF\"\n ) {\n this.moveOneRight();\n event.preventDefault();\n\n // Bring to front: Cmd-Shift-F\n } else if (event.metaKey && event.shiftKey && event.code === \"KeyF\") {\n this.moveAllRight();\n event.preventDefault();\n\n // Select all: Cmd-A\n } else if (event.metaKey && event.code === \"KeyA\") {\n elements.forEach(element => {\n element.isSelected = true;\n });\n this.forceUpdate();\n event.preventDefault();\n } else if (shapesShortcutKeys.includes(event.key.toLowerCase())) {\n this.setState({ elementType: findElementByKey(event.key) });\n } else if (event.metaKey && event.code === \"KeyZ\") {\n let lastEntry = stateHistory.pop();\n // If nothing was changed since last, take the previous one\n if (generateHistoryCurrentEntry() === lastEntry) {\n lastEntry = stateHistory.pop();\n }\n if (lastEntry !== undefined) {\n restoreHistoryEntry(lastEntry);\n }\n this.forceUpdate();\n event.preventDefault();\n }\n };\n\n private deleteSelectedElements = () => {\n deleteSelectedElements();\n this.forceUpdate();\n };\n\n private clearCanvas = () => {\n if (window.confirm(\"This will clear the whole canvas. Are you sure?\")) {\n elements.splice(0, elements.length);\n this.setState({\n viewBackgroundColor: \"#ffffff\",\n scrollX: 0,\n scrollY: 0\n });\n this.forceUpdate();\n }\n };\n\n private moveAllLeft = () => {\n moveAllLeft(elements, getSelectedIndices());\n this.forceUpdate();\n };\n\n private moveOneLeft = () => {\n moveOneLeft(elements, getSelectedIndices());\n this.forceUpdate();\n };\n\n private moveAllRight = () => {\n moveAllRight(elements, getSelectedIndices());\n this.forceUpdate();\n };\n\n private moveOneRight = () => {\n moveOneRight(elements, getSelectedIndices());\n this.forceUpdate();\n };\n\n private removeWheelEventListener: (() => void) | undefined;\n\n public render() {\n const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT;\n const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP;\n\n return (\n <div\n className=\"container\"\n onCut={e => {\n e.clipboardData.setData(\n \"text/plain\",\n JSON.stringify(elements.filter(element => element.isSelected))\n );\n deleteSelectedElements();\n this.forceUpdate();\n e.preventDefault();\n }}\n onCopy={e => {\n e.clipboardData.setData(\n \"text/plain\",\n JSON.stringify(elements.filter(element => element.isSelected))\n );\n e.preventDefault();\n }}\n onPaste={e => {\n const paste = e.clipboardData.getData(\"text\");\n let parsedElements;\n try {\n parsedElements = JSON.parse(paste);\n } catch (e) {}\n if (\n Array.isArray(parsedElements) &&\n parsedElements.length > 0 &&\n parsedElements[0].type // need to implement a better check here...\n ) {\n clearSelection();\n parsedElements.forEach(parsedElement => {\n parsedElement.x += 10;\n parsedElement.y += 10;\n parsedElement.seed = randomSeed();\n generateDraw(parsedElement);\n elements.push(parsedElement);\n });\n this.forceUpdate();\n }\n e.preventDefault();\n }}\n >\n <div className=\"sidePanel\">\n <h4>Shapes</h4>\n <div className=\"panelTools\">\n {SHAPES.map(({ value, icon }) => (\n <label key={value} className=\"tool\">\n <input\n type=\"radio\"\n checked={this.state.elementType === value}\n onChange={() => {\n this.setState({ elementType: value });\n clearSelection();\n document.documentElement.style.cursor =\n value === \"text\" ? \"text\" : \"crosshair\";\n this.forceUpdate();\n }}\n />\n <div className=\"toolIcon\">{icon}</div>\n </label>\n ))}\n </div>\n <h4>Colors</h4>\n <div className=\"panelColumn\">\n <label>\n <input\n type=\"color\"\n value={this.state.viewBackgroundColor}\n onChange={e => {\n this.setState({ viewBackgroundColor: e.target.value });\n }}\n />\n Background\n </label>\n <label>\n <input\n type=\"color\"\n value={this.state.currentItemStrokeColor}\n onChange={e => {\n this.setState({ currentItemStrokeColor: e.target.value });\n }}\n />\n Shape Stroke\n </label>\n <label>\n <input\n type=\"color\"\n value={this.state.currentItemBackgroundColor}\n onChange={e => {\n this.setState({ currentItemBackgroundColor: e.target.value });\n }}\n />\n Shape Background\n </label>\n </div>\n <h4>Canvas</h4>\n <div className=\"panelColumn\">\n <button\n onClick={this.clearCanvas}\n title=\"Clear the canvas & reset background color\"\n >\n Clear canvas\n </button>\n </div>\n <h4>Export</h4>\n <div className=\"panelColumn\">\n <button\n onClick={() => {\n exportAsPNG(this.state);\n }}\n >\n Export to png\n </button>\n <label>\n <input\n type=\"checkbox\"\n checked={this.state.exportBackground}\n onChange={e => {\n this.setState({ exportBackground: e.target.checked });\n }}\n />\n background\n </label>\n </div>\n <h4>Save/Load</h4>\n <div className=\"panelColumn\">\n <button\n onClick={() => {\n saveAsJSON();\n }}\n >\n Save as...\n </button>\n <button\n onClick={() => {\n loadFromJSON().then(() => this.forceUpdate());\n }}\n >\n Load file...\n </button>\n </div>\n {someElementIsSelected() && (\n <>\n <h4>Shape options</h4>\n <div className=\"panelColumn\">\n <button onClick={this.deleteSelectedElements}>Delete</button>\n <button onClick={this.moveOneRight}>Bring forward</button>\n <button onClick={this.moveAllRight}>Bring to front</button>\n <button onClick={this.moveOneLeft}>Send backward</button>\n <button onClick={this.moveAllLeft}>Send to back</button>\n </div>\n </>\n )}\n </div>\n <canvas\n id=\"canvas\"\n style={{\n width: canvasWidth,\n height: canvasHeight\n }}\n width={canvasWidth * window.devicePixelRatio}\n height={canvasHeight * window.devicePixelRatio}\n ref={canvas => {\n if (this.removeWheelEventListener) {\n this.removeWheelEventListener();\n this.removeWheelEventListener = undefined;\n }\n if (canvas) {\n canvas.addEventListener(\"wheel\", this.handleWheel, {\n passive: false\n });\n this.removeWheelEventListener = () =>\n canvas.removeEventListener(\"wheel\", this.handleWheel);\n\n // Whenever React sets the width/height of the canvas element,\n // the context loses the scale transform. We need to re-apply it\n if (\n canvasWidth !== lastCanvasWidth ||\n canvasHeight !== lastCanvasHeight\n ) {\n lastCanvasWidth = canvasWidth;\n lastCanvasHeight = canvasHeight;\n canvas\n .getContext(\"2d\")!\n .scale(window.devicePixelRatio, window.devicePixelRatio);\n }\n }\n }}\n onMouseDown={e => {\n if (lastMouseUp !== null) {\n // Unfortunately, sometimes we don't get a mouseup after a mousedown,\n // this can happen when a contextual menu or alert is triggered. In order to avoid\n // being in a weird state, we clean up on the next mousedown\n lastMouseUp(e);\n }\n // only handle left mouse button\n if (e.button !== 0) return;\n // fixes mousemove causing selection of UI texts #32\n e.preventDefault();\n // Preventing the event above disables default behavior\n // of defocusing potentially focused input, which is what we want\n // when clicking inside the canvas.\n if (isInputLike(document.activeElement)) {\n document.activeElement.blur();\n }\n\n const x =\n e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX;\n const y = e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;\n const element = newElement(\n this.state.elementType,\n x,\n y,\n this.state.currentItemStrokeColor,\n this.state.currentItemBackgroundColor\n );\n let resizeHandle: string | false = false;\n let isDraggingElements = false;\n let isResizingElements = false;\n if (this.state.elementType === \"selection\") {\n const resizeElement = elements.find(element => {\n return resizeTest(element, x, y, {\n scrollX: this.state.scrollX,\n scrollY: this.state.scrollY,\n viewBackgroundColor: this.state.viewBackgroundColor\n });\n });\n\n this.setState({\n resizingElement: resizeElement ? resizeElement : null\n });\n\n if (resizeElement) {\n resizeHandle = resizeTest(resizeElement, x, y, {\n scrollX: this.state.scrollX,\n scrollY: this.state.scrollY,\n viewBackgroundColor: this.state.viewBackgroundColor\n });\n document.documentElement.style.cursor = `${resizeHandle}-resize`;\n isResizingElements = true;\n } else {\n let hitElement = null;\n // We need to to hit testing from front (end of the array) to back (beginning of the array)\n for (let i = elements.length - 1; i >= 0; --i) {\n if (hitTest(elements[i], x, y)) {\n hitElement = elements[i];\n break;\n }\n }\n\n // If we click on something\n if (hitElement) {\n if (hitElement.isSelected) {\n // If that element is not already selected, do nothing,\n // we're likely going to drag it\n } else {\n // We unselect every other elements unless shift is pressed\n if (!e.shiftKey) {\n clearSelection();\n }\n // No matter what, we select it\n hitElement.isSelected = true;\n }\n } else {\n // If we don't click on anything, let's remove all the selected elements\n clearSelection();\n }\n\n isDraggingElements = someElementIsSelected();\n\n if (isDraggingElements) {\n document.documentElement.style.cursor = \"move\";\n }\n }\n }\n\n if (isTextElement(element)) {\n resetCursor();\n const text = prompt(\"What text do you want?\");\n if (text === null) {\n return;\n }\n element.text = text;\n element.font = \"20px Virgil\";\n const font = context.font;\n context.font = element.font;\n const {\n actualBoundingBoxAscent,\n actualBoundingBoxDescent,\n width\n } = context.measureText(element.text);\n element.actualBoundingBoxAscent = actualBoundingBoxAscent;\n context.font = font;\n const height = actualBoundingBoxAscent + actualBoundingBoxDescent;\n // Center the text\n element.x -= width / 2;\n element.y -= actualBoundingBoxAscent;\n element.width = width;\n element.height = height;\n }\n\n generateDraw(element);\n elements.push(element);\n if (this.state.elementType === \"text\") {\n this.setState({\n draggingElement: null,\n elementType: \"selection\"\n });\n element.isSelected = true;\n } else {\n this.setState({ draggingElement: element });\n }\n\n let lastX = x;\n let lastY = y;\n\n const onMouseMove = (e: MouseEvent) => {\n const target = e.target;\n if (!(target instanceof HTMLElement)) {\n return;\n }\n\n if (isResizingElements && this.state.resizingElement) {\n const el = this.state.resizingElement;\n const selectedElements = elements.filter(el => el.isSelected);\n if (selectedElements.length === 1) {\n const x =\n e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX;\n const y =\n e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;\n selectedElements.forEach(element => {\n switch (resizeHandle) {\n case \"nw\":\n element.width += element.x - lastX;\n element.height += element.y - lastY;\n element.x = lastX;\n element.y = lastY;\n break;\n case \"ne\":\n element.width = lastX - element.x;\n element.height += element.y - lastY;\n element.y = lastY;\n break;\n case \"sw\":\n element.width += element.x - lastX;\n element.x = lastX;\n element.height = lastY - element.y;\n break;\n case \"se\":\n element.width += x - lastX;\n if (e.shiftKey) {\n element.height = element.width;\n } else {\n element.height += y - lastY;\n }\n break;\n case \"n\":\n element.height += element.y - lastY;\n element.y = lastY;\n break;\n case \"w\":\n element.width += element.x - lastX;\n element.x = lastX;\n break;\n case \"s\":\n element.height = lastY - element.y;\n break;\n case \"e\":\n element.width = lastX - element.x;\n break;\n }\n\n el.x = element.x;\n el.y = element.y;\n generateDraw(el);\n });\n lastX = x;\n lastY = y;\n // We don't want to save history when resizing an element\n skipHistory = true;\n this.forceUpdate();\n return;\n }\n }\n\n if (isDraggingElements) {\n const selectedElements = elements.filter(el => el.isSelected);\n if (selectedElements.length) {\n const x =\n e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX;\n const y =\n e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;\n selectedElements.forEach(element => {\n element.x += x - lastX;\n element.y += y - lastY;\n });\n lastX = x;\n lastY = y;\n // We don't want to save history when dragging an element to initially size it\n skipHistory = true;\n this.forceUpdate();\n return;\n }\n }\n\n // It is very important to read this.state within each move event,\n // otherwise we would read a stale one!\n const draggingElement = this.state.draggingElement;\n if (!draggingElement) return;\n let width =\n e.clientX -\n CANVAS_WINDOW_OFFSET_LEFT -\n draggingElement.x -\n this.state.scrollX;\n let height =\n e.clientY -\n CANVAS_WINDOW_OFFSET_TOP -\n draggingElement.y -\n this.state.scrollY;\n draggingElement.width = width;\n // Make a perfect square or circle when shift is enabled\n draggingElement.height = e.shiftKey ? width : height;\n\n generateDraw(draggingElement);\n\n if (this.state.elementType === \"selection\") {\n setSelection(draggingElement);\n }\n // We don't want to save history when moving an element\n skipHistory = true;\n this.forceUpdate();\n };\n\n const onMouseUp = (e: MouseEvent) => {\n const { draggingElement, elementType } = this.state;\n\n lastMouseUp = null;\n window.removeEventListener(\"mousemove\", onMouseMove);\n window.removeEventListener(\"mouseup\", onMouseUp);\n\n resetCursor();\n\n // if no element is clicked, clear the selection and redraw\n if (draggingElement === null) {\n clearSelection();\n this.forceUpdate();\n return;\n }\n\n if (elementType === \"selection\") {\n if (isDraggingElements) {\n isDraggingElements = false;\n }\n elements.pop();\n } else {\n draggingElement.isSelected = true;\n }\n\n this.setState({\n draggingElement: null,\n elementType: \"selection\"\n });\n this.forceUpdate();\n };\n\n lastMouseUp = onMouseUp;\n\n window.addEventListener(\"mousemove\", onMouseMove);\n window.addEventListener(\"mouseup\", onMouseUp);\n\n // We don't want to save history on mouseDown, only on mouseUp when it's fully configured\n skipHistory = true;\n this.forceUpdate();\n }}\n />\n </div>\n );\n }\n\n private handleWheel = (e: WheelEvent) => {\n e.preventDefault();\n const { deltaX, deltaY } = e;\n this.setState(state => ({\n scrollX: state.scrollX - deltaX,\n scrollY: state.scrollY - deltaY\n }));\n };\n\n componentDidUpdate() {\n renderScene(rc, canvas, {\n scrollX: this.state.scrollX,\n scrollY: this.state.scrollY,\n viewBackgroundColor: this.state.viewBackgroundColor\n });\n save(this.state);\n if (!skipHistory) {\n pushHistoryEntry(generateHistoryCurrentEntry());\n }\n skipHistory = false;\n }\n}\n\nconst rootElement = document.getElementById(\"root\");\nReactDOM.render(<App />, rootElement);\nconst canvas = document.getElementById(\"canvas\") as HTMLCanvasElement;\nconst rc = rough.canvas(canvas);\nconst context = canvas.getContext(\"2d\")!;\n\nReactDOM.render(<App />, rootElement);\n"],"sourceRoot":""}