From 53994e71e5e1a94cb291925365a615094fd6fcd9 Mon Sep 17 00:00:00 2001 From: Lipis <lipiridis@gmail.com> Date: Sun, 2 Feb 2020 20:04:35 +0200 Subject: [PATCH] Add more ESLint rules and change the formatting scripts (#626) * Add curly rule in ESLint for consistency * Fix rules * More rules * REturn * Push * no else return * prefer const * destructing --- .github/workflows/lint.yml | 1 + .lintstagedrc.js | 5 +- package-lock.json | 41 ++++++++++++++ package.json | 35 ++++++++++-- src/actions/manager.tsx | 4 +- src/components/ColorPicker.tsx | 14 +++-- src/components/ExportDialog.tsx | 2 +- src/components/FixedSideContainer.tsx | 2 +- src/components/Popover.tsx | 8 ++- src/components/ProjectName.tsx | 4 +- src/components/ToolButton.tsx | 3 +- src/element/collision.ts | 25 +++++---- src/element/handlerRectangles.ts | 2 +- src/element/resizeTest.ts | 8 ++- src/element/textWysiwyg.tsx | 4 +- src/i18n.ts | 4 +- src/index.tsx | 79 +++++++++++++++++++-------- src/renderer/renderElement.ts | 4 +- src/renderer/renderScene.ts | 20 ++++--- src/scene/data.ts | 10 ++-- src/shapes.tsx | 3 +- 21 files changed, 201 insertions(+), 77 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 918bf8b3cd..a5e3c84f8f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,6 +17,7 @@ jobs: - name: Install and lint run: | npm ci + npm run test:other npm run test:code env: CI: true diff --git a/.lintstagedrc.js b/.lintstagedrc.js index 87a02c84d8..18e1d218e9 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -7,9 +7,8 @@ const cli = new CLIEngine({}); module.exports = { "*.{js,ts,tsx}": files => { return ( - "eslint --max-warnings=0 " + - files.filter(file => !cli.isPathIgnored(file)).join(" ") + "eslint --fix" + files.filter(file => !cli.isPathIgnored(file)).join(" ") ); }, - "*.{js,css,scss,json,md,ts,tsx,html,yml}": ["prettier --write"], + "*.{css,scss,json,md,html,yml}": ["prettier --write"], }; diff --git a/package-lock.json b/package-lock.json index c6dc04f433..1fc882dafc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5299,6 +5299,23 @@ } } }, + "eslint-config-prettier": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, "eslint-config-react-app": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.1.0.tgz", @@ -5583,6 +5600,15 @@ } } }, + "eslint-plugin-prettier": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.16.0.tgz", @@ -5976,6 +6002,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", @@ -12119,6 +12151,15 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", diff --git a/package.json b/package.json index ea80804179..26fed6faee 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,9 @@ "@types/nanoid": "2.1.0", "@types/react": "16.9.19", "@types/react-dom": "16.9.5", + "eslint": "6.8.0", + "eslint-config-prettier": "6.10.0", + "eslint-plugin-prettier": "3.1.2", "husky": "4.2.1", "lint-staged": "10.0.3", "node-sass": "4.13.1", @@ -28,10 +31,17 @@ "typescript": "3.7.5" }, "eslintConfig": { - "extends": "react-app", + "extends": [ + "prettier", + "react-app" + ], + "plugins": [ + "prettier" + ], "rules": { + "curly": "error", "no-console": [ - "warn", + "error", { "allow": [ "warn", @@ -39,7 +49,17 @@ "info" ] } - ] + ], + "no-else-return": "error", + "no-useless-return": "error", + "prefer-const": [ + "error", + { + "destructuring": "all" + } + ], + "prefer-template": "error", + "prettier/prettier": "error" } }, "homepage": "https://excalidraw.com", @@ -54,12 +74,15 @@ "build": "react-scripts build", "build-node": "./scripts/build-node.js", "eject": "react-scripts eject", - "fix": "npm run prettier -- --write", - "prettier": "prettier \"**/*.{js,css,scss,json,md,ts,tsx,html,yml}\" --ignore-path=.eslintignore", + "fix": "npm run fix:other && npm run fix:code", + "fix:code": "npm run test:code -- --fix", + "fix:other": "npm run prettier -- --write", + "prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore", "start": "react-scripts start", "test": "npm run test:app", "test:app": "react-scripts test --env=jsdom --passWithNoTests", - "test:code": "npm run prettier -- --list-different" + "test:code": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", + "test:other": "npm run prettier -- --list-different" }, "version": "1.0.0", "license": "MIT", diff --git a/src/actions/manager.tsx b/src/actions/manager.tsx index ace170d9e7..399fd70d4e 100644 --- a/src/actions/manager.tsx +++ b/src/actions/manager.tsx @@ -37,7 +37,9 @@ export class ActionManager implements ActionsManagerInterface { action => action.keyTest && action.keyTest(event, appState, elements), ); - if (data.length === 0) return null; + if (data.length === 0) { + return null; + } event.preventDefault(); return data[0].perform(elements, appState, null); diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 7e670e8d06..d505f0a323 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -109,7 +109,9 @@ const Picker = function({ <div className="colors-gallery" ref={el => { - if (el) gallery.current = el; + if (el) { + gallery.current = el; + } }} > {colors.map((_color, i) => ( @@ -124,8 +126,12 @@ const Picker = function({ style={{ backgroundColor: _color }} key={_color} ref={el => { - if (el && i === 0) firstItem.current = el; - if (el && _color === color) activeItem.current = el; + if (el && i === 0) { + firstItem.current = el; + } + if (el && _color === color) { + activeItem.current = el; + } }} onFocus={() => { onChange(_color); @@ -186,7 +192,7 @@ const ColorInput = React.forwardRef( onChange={e => { const value = e.target.value.toLowerCase(); if (value.match(colorRegex)) { - onChange(value === "transparent" ? "transparent" : "#" + value); + onChange(value === "transparent" ? "transparent" : `#${value}`); } setInnerValue(value); }} diff --git a/src/components/ExportDialog.tsx b/src/components/ExportDialog.tsx index 4458941260..7eeaa8957e 100644 --- a/src/components/ExportDialog.tsx +++ b/src/components/ExportDialog.tsx @@ -178,7 +178,7 @@ function ExportModal({ key={s} size="s" type="radio" - icon={"x" + s} + icon={`x${s}`} name="export-canvas-scale" aria-label={`Scale ${s} x`} id="export-canvas-scale" diff --git a/src/components/FixedSideContainer.tsx b/src/components/FixedSideContainer.tsx index 8a49de5159..cc37144a44 100644 --- a/src/components/FixedSideContainer.tsx +++ b/src/components/FixedSideContainer.tsx @@ -12,7 +12,7 @@ export function FixedSideContainer({ side, }: FixedSideContainerProps) { return ( - <div className={"FixedSideContainer FixedSideContainer_side_" + side}> + <div className={`FixedSideContainer FixedSideContainer_side_${side}`}> {children} </div> ); diff --git a/src/components/Popover.tsx b/src/components/Popover.tsx index 7b76af14a3..6e62e03f2c 100644 --- a/src/components/Popover.tsx +++ b/src/components/Popover.tsx @@ -26,11 +26,11 @@ export function Popover({ const viewportWidth = window.innerWidth; if (x + width > viewportWidth) { - element.style.left = viewportWidth - width + "px"; + element.style.left = `${viewportWidth - width}px`; } const viewportHeight = window.innerHeight; if (y + height > viewportHeight) { - element.style.top = viewportHeight - height + "px"; + element.style.top = `${viewportHeight - height}px`; } } }, [fitInViewport]); @@ -42,7 +42,9 @@ export function Popover({ onClick={onCloseRequest} onContextMenu={e => { e.preventDefault(); - if (onCloseRequest) onCloseRequest(); + if (onCloseRequest) { + onCloseRequest(); + } }} /> {children} diff --git a/src/components/ProjectName.tsx b/src/components/ProjectName.tsx index f13d154ade..9696ab45d5 100644 --- a/src/components/ProjectName.tsx +++ b/src/components/ProjectName.tsx @@ -16,7 +16,9 @@ export class ProjectName extends Component<Props> { private handleBlur = (e: React.FocusEvent<HTMLElement>) => { const value = e.currentTarget.innerText.trim(); - if (value !== this.props.value) this.props.onChange(value); + if (value !== this.props.value) { + this.props.onChange(value); + } removeSelection(); }; diff --git a/src/components/ToolButton.tsx b/src/components/ToolButton.tsx index 120a3f83f7..62c89c0b69 100644 --- a/src/components/ToolButton.tsx +++ b/src/components/ToolButton.tsx @@ -35,7 +35,7 @@ export const ToolButton = React.forwardRef(function( React.useImperativeHandle(ref, () => innerRef.current); const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`; - if (props.type === "button") + if (props.type === "button") { return ( <button className={`ToolIcon_type_button ToolIcon ${sizeCn}`} @@ -50,6 +50,7 @@ export const ToolButton = React.forwardRef(function( </div> </button> ); + } return ( <label className="ToolIcon" title={props.title}> diff --git a/src/element/collision.ts b/src/element/collision.ts index 97965f02da..9133f39330 100644 --- a/src/element/collision.ts +++ b/src/element/collision.ts @@ -61,9 +61,8 @@ export function hitTest( return ( a * tx - (px - lineThreshold) >= 0 && b * ty - (py - lineThreshold) >= 0 ); - } else { - return Math.hypot(a * tx - px, b * ty - py) < lineThreshold; } + return Math.hypot(a * tx - px, b * ty - py) < lineThreshold; } else if (element.type === "rectangle") { const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); @@ -88,7 +87,6 @@ export function hitTest( } else if (element.type === "diamond") { x -= element.x; y -= element.y; - let [ topX, topY, @@ -102,8 +100,12 @@ export function hitTest( if (isElementDraggableFromInside(element)) { // TODO: remove this when we normalize coordinates globally - if (topY > bottomY) [bottomY, topY] = [topY, bottomY]; - if (rightX < leftX) [leftX, rightX] = [rightX, leftX]; + if (topY > bottomY) { + [bottomY, topY] = [topY, bottomY]; + } + if (rightX < leftX) { + [leftX, rightX] = [rightX, leftX]; + } topY -= lineThreshold; bottomY += lineThreshold; @@ -153,10 +155,14 @@ export function hitTest( const shape = element.shape as Drawable[]; // If shape does not consist of curve and two line segments // for arrow shape, return false - if (shape.length < 3) return false; + if (shape.length < 3) { + return false; + } const [x1, y1, x2, y2] = getArrowAbsoluteBounds(element); - if (x < x1 || y < y1 - 10 || x > x2 || y > y2 + 10) return false; + if (x < x1 || y < y1 - 10 || x > x2 || y > y2 + 10) { + return false; + } const relX = x - element.x; const relY = y - element.y; @@ -181,9 +187,8 @@ export function hitTest( } else if (element.type === "selection") { console.warn("This should not happen, we need to investigate why it does."); return false; - } else { - throw new Error("Unimplemented type " + element.type); } + throw new Error(`Unimplemented type ${element.type}`); } const pointInBezierEquation = ( @@ -249,7 +254,7 @@ const hitTestRoughShape = (opSet: OpSet[], x: number, y: number) => { // check if points are on the curve // cubic bezier curves require four parameters // the first parameter is the last stored position (p0) - let retVal = pointInBezierEquation(p0, p1, p2, p3, [x, y]); + const retVal = pointInBezierEquation(p0, p1, p2, p3, [x, y]); // set end point of bezier curve as the new starting point for // upcoming operations as each operation is based on the last drawn diff --git a/src/element/handlerRectangles.ts b/src/element/handlerRectangles.ts index d202fd81bd..a12842a39f 100644 --- a/src/element/handlerRectangles.ts +++ b/src/element/handlerRectangles.ts @@ -15,7 +15,7 @@ export function handlerRectangles( let marginX = -8; let marginY = -8; - let minimumSize = 40; + const minimumSize = 40; if (element.type === "arrow") { [elementX1, elementY1, elementX2, elementY2] = getArrowAbsoluteBounds( element, diff --git a/src/element/resizeTest.ts b/src/element/resizeTest.ts index 532b9b94f7..3d7ae80752 100644 --- a/src/element/resizeTest.ts +++ b/src/element/resizeTest.ts @@ -11,13 +11,17 @@ export function resizeTest( y: number, { scrollX, scrollY }: SceneScroll, ): HandlerRectanglesRet | false { - if (!element.isSelected || element.type === "text") return false; + if (!element.isSelected || element.type === "text") { + return false; + } const handlers = handlerRectangles(element, { scrollX, scrollY }); const filter = Object.keys(handlers).filter(key => { const handler = handlers[key as HandlerRectanglesRet]!; - if (!handler) return false; + if (!handler) { + return false; + } return ( x + scrollX >= handler[0] && diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index cdeeac21ca..ba6e6eaf8f 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -41,8 +41,8 @@ export function textWysiwyg({ color: strokeColor, position: "fixed", opacity: opacity / 100, - top: y + "px", - left: x + "px", + top: `${y}px`, + left: `${x}px`, transform: "translate(-50%, -50%)", textAlign: "left", display: "inline-block", diff --git a/src/i18n.ts b/src/i18n.ts index 5a40e32cd5..1889af33f8 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -43,12 +43,12 @@ export function t(path: string, replacement?: { [key: string]: string }) { findPartsForData(currentLanguage.data, parts) || findPartsForData(fallbackLanguage.data, parts); if (translation === undefined) { - throw new Error("Can't find translation for " + path); + throw new Error(`Can't find translation for ${path}`); } if (replacement) { for (var key in replacement) { - translation = translation.replace("{{" + key + "}}", replacement[key]); + translation = translation.replace(`{{${key}}}`, replacement[key]); } } return translation; diff --git a/src/index.tsx b/src/index.tsx index 55cc28bc26..98f10edffd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -221,7 +221,9 @@ export class App extends React.Component<any, AppState> { }; private onCut = (e: ClipboardEvent) => { - if (isInputLike(e.target) && !isToolIcon(e.target)) return; + if (isInputLike(e.target) && !isToolIcon(e.target)) { + return; + } e.clipboardData?.setData( "text/plain", JSON.stringify( @@ -235,7 +237,9 @@ export class App extends React.Component<any, AppState> { e.preventDefault(); }; private onCopy = (e: ClipboardEvent) => { - if (isInputLike(e.target) && !isToolIcon(e.target)) return; + if (isInputLike(e.target) && !isToolIcon(e.target)) { + return; + } e.clipboardData?.setData( "text/plain", JSON.stringify( @@ -247,7 +251,9 @@ export class App extends React.Component<any, AppState> { e.preventDefault(); }; private onPaste = (e: ClipboardEvent) => { - if (isInputLike(e.target) && !isToolIcon(e.target)) return; + if (isInputLike(e.target) && !isToolIcon(e.target)) { + return; + } const paste = e.clipboardData?.getData("text") || ""; this.addElementsFromPaste(paste); e.preventDefault(); @@ -339,7 +345,9 @@ export class App extends React.Component<any, AppState> { }; private onKeyDown = (event: KeyboardEvent) => { - if (isInputLike(event.target) && event.key !== KEYS.ESCAPE) return; + if (isInputLike(event.target) && event.key !== KEYS.ESCAPE) { + return; + } const actionResult = this.actionManager.handleKeyDown( event, @@ -349,7 +357,9 @@ export class App extends React.Component<any, AppState> { if (actionResult) { this.syncActionResult(actionResult); - if (actionResult) return; + if (actionResult) { + return; + } } const shape = findShapeByKey(event.key); @@ -361,10 +371,15 @@ export class App extends React.Component<any, AppState> { elements = elements.map(el => { if (el.isSelected) { const element = { ...el }; - if (event.key === KEYS.ARROW_LEFT) element.x -= step; - else if (event.key === KEYS.ARROW_RIGHT) element.x += step; - else if (event.key === KEYS.ARROW_UP) element.y -= step; - else if (event.key === KEYS.ARROW_DOWN) element.y += step; + if (event.key === KEYS.ARROW_LEFT) { + element.x -= step; + } else if (event.key === KEYS.ARROW_RIGHT) { + element.x += step; + } else if (event.key === KEYS.ARROW_UP) { + element.y -= step; + } else if (event.key === KEYS.ARROW_DOWN) { + element.y += step; + } return element; } return el; @@ -602,13 +617,14 @@ export class App extends React.Component<any, AppState> { actionManager={this.actionManager} syncActionResult={this.syncActionResult} onExportToPng={(exportedElements, scale) => { - if (this.canvas) + if (this.canvas) { exportCanvas("png", exportedElements, this.canvas, { exportBackground: this.state.exportBackground, name: this.state.name, viewBackgroundColor: this.state.viewBackgroundColor, scale, }); + } }} onExportToSvg={(exportedElements, scale) => { if (this.canvas) { @@ -621,16 +637,17 @@ export class App extends React.Component<any, AppState> { } }} onExportToClipboard={(exportedElements, scale) => { - if (this.canvas) + if (this.canvas) { exportCanvas("clipboard", exportedElements, this.canvas, { exportBackground: this.state.exportBackground, name: this.state.name, viewBackgroundColor: this.state.viewBackgroundColor, scale, }); + } }} onExportToBackend={exportedElements => { - if (this.canvas) + if (this.canvas) { exportCanvas( "backend", exportedElements.map(element => ({ @@ -640,6 +657,7 @@ export class App extends React.Component<any, AppState> { this.canvas, this.state, ); + } }} /> {this.actionManager.renderAction( @@ -813,7 +831,9 @@ export class App extends React.Component<any, AppState> { lastMouseUp(e); } - if (isPanning) return; + if (isPanning) { + return; + } // pan canvas on wheel button drag or space+drag if ( @@ -826,8 +846,8 @@ export class App extends React.Component<any, AppState> { document.documentElement.style.cursor = CURSOR_TYPE.GRABBING; let { clientX: lastX, clientY: lastY } = e; const onMouseMove = (e: MouseEvent) => { - let deltaX = lastX - e.clientX; - let deltaY = lastY - e.clientY; + const deltaX = lastX - e.clientX; + const deltaY = lastY - e.clientY; lastX = e.clientX; lastY = e.clientY; // We don't want to save history when panning around @@ -857,7 +877,9 @@ export class App extends React.Component<any, AppState> { } // only handle left mouse button - if (e.button !== MOUSE_BUTTON.MAIN) return; + if (e.button !== MOUSE_BUTTON.MAIN) { + return; + } // fixes mousemove causing selection of UI texts #32 e.preventDefault(); // Preventing the event above disables default behavior @@ -1080,7 +1102,7 @@ export class App extends React.Component<any, AppState> { const absPx = p1[0] + element.x; const absPy = p1[1] + element.y; - let { width, height } = getPerfectElementSize( + const { width, height } = getPerfectElementSize( "arrow", mouseX - element.x - p1[0], mouseY - element.y - p1[1], @@ -1155,8 +1177,9 @@ export class App extends React.Component<any, AppState> { // triggering mousemove) if (!draggingOccurred && this.state.elementType === "arrow") { const { x, y } = viewportCoordsToSceneCoords(e, this.state); - if (distance2d(x, y, originX, originY) < DRAGGING_THRESHOLD) + if (distance2d(x, y, originX, originY) < DRAGGING_THRESHOLD) { return; + } } if (isResizingElements && this.state.resizingElement) { @@ -1432,7 +1455,9 @@ export class App extends React.Component<any, AppState> { // 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; + if (!draggingElement) { + return; + } const { x, y } = viewportCoordsToSceneCoords(e, this.state); @@ -1443,8 +1468,12 @@ export class App extends React.Component<any, AppState> { this.state.elementType === "line" || this.state.elementType === "arrow"; - if (isLinear && x < originX) width = -width; - if (isLinear && y < originY) height = -height; + if (isLinear && x < originX) { + width = -width; + } + if (isLinear && y < originY) { + height = -height; + } if (e.shiftKey) { ({ width, height } = getPerfectElementSize( @@ -1453,7 +1482,9 @@ export class App extends React.Component<any, AppState> { !isLinear && y < originY ? -height : height, )); - if (!isLinear && height < 0) height = -height; + if (!isLinear && height < 0) { + height = -height; + } } if (!isLinear) { @@ -1724,7 +1755,9 @@ export class App extends React.Component<any, AppState> { }); }} onMouseMove={e => { - if (isHoldingSpace || isPanning) return; + if (isHoldingSpace || isPanning) { + return; + } const hasDeselectedButton = Boolean(e.buttons); if ( hasDeselectedButton || diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 0e9d175ab7..e42c871db5 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -176,7 +176,7 @@ export function renderElement( context.font = font; context.globalAlpha = 1; } else { - throw new Error("Unimplemented type " + element.type); + throw new Error(`Unimplemented type ${element.type}`); } } } @@ -267,7 +267,7 @@ export function renderElementToSvg( } svgRoot.appendChild(node); } else { - throw new Error("Unimplemented type " + element.type); + throw new Error(`Unimplemented type ${element.type}`); } } } diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 096392520e..c63073bc8e 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -32,7 +32,9 @@ export function renderScene( renderSelection?: boolean; } = {}, ) { - if (!canvas) return false; + if (!canvas) { + return false; + } const context = canvas.getContext("2d")!; const fillStyle = context.fillStyle; @@ -130,7 +132,7 @@ export function renderScene( context.fillStyle = SCROLLBAR_COLOR; context.strokeStyle = "rgba(255,255,255,0.8)"; [scrollBars.horizontal, scrollBars.vertical].forEach(scrollBar => { - if (scrollBar) + if (scrollBar) { roundRect( context, scrollBar.x, @@ -139,6 +141,7 @@ export function renderScene( scrollBar.height, SCROLLBAR_WIDTH / 2, ); + } }); context.strokeStyle = strokeStyle; context.fillStyle = fillStyle; @@ -161,14 +164,13 @@ function isVisibleElement( x2 += scrollX; y2 += scrollY; return x2 >= 0 && x1 <= canvasWidth && y2 >= 0 && y1 <= canvasHeight; - } else { - return ( - x2 + scrollX >= 0 && - x1 + scrollX <= canvasWidth && - y2 + scrollY >= 0 && - y1 + scrollY <= canvasHeight - ); } + return ( + x2 + scrollX >= 0 && + x1 + scrollX <= canvasWidth && + y2 + scrollY >= 0 && + y1 + scrollY <= canvasHeight + ); } // This should be only called for exporting purposes diff --git a/src/scene/data.ts b/src/scene/data.ts index efe69cdd3c..4f2a713b15 100644 --- a/src/scene/data.ts +++ b/src/scene/data.ts @@ -49,7 +49,7 @@ export function serializeAsJSON( export function calculateScrollCenter( elements: readonly ExcalidrawElement[], ): { scrollX: number; scrollY: number } { - let [x1, y1, x2, y2] = getCommonBounds(elements); + const [x1, y1, x2, y2] = getCommonBounds(elements); const centerX = (x1 + x2) / 2; const centerY = (y1 + y2) / 2; @@ -149,7 +149,6 @@ export async function exportToBackend( } } catch (e) { window.alert(t("alerts.couldNotCreateShareableLink")); - return; } } @@ -194,8 +193,9 @@ export async function exportCanvas( scale?: number; }, ) { - if (!elements.length) + if (!elements.length) { return window.alert(t("alerts.cannotExportEmptyCanvas")); + } // calculate smallest area to fit the contents in if (type === "svg") { @@ -252,7 +252,9 @@ export async function exportCanvas( } // clean up the DOM - if (tempCanvas !== canvas) tempCanvas.remove(); + if (tempCanvas !== canvas) { + tempCanvas.remove(); + } } function restore( diff --git a/src/shapes.tsx b/src/shapes.tsx index 942e018bc4..91db7faaf6 100644 --- a/src/shapes.tsx +++ b/src/shapes.tsx @@ -75,8 +75,9 @@ export const shapesShortcutKeys = SHAPES.map((shape, index) => [ export function findShapeByKey(key: string) { const defaultElement = "selection"; return SHAPES.reduce((element, shape, index) => { - if (shape.value[0] !== key && key !== (index + 1).toString()) + if (shape.value[0] !== key && key !== (index + 1).toString()) { return element; + } return shape.value; }, defaultElement);