mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-31 02:44:50 +01:00 
			
		
		
		
	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
This commit is contained in:
		
							
								
								
									
										1
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -17,6 +17,7 @@ jobs: | ||||
|       - name: Install and lint | ||||
|         run: | | ||||
|           npm ci | ||||
|           npm run test:other | ||||
|           npm run test:code | ||||
|         env: | ||||
|           CI: true | ||||
|   | ||||
| @@ -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"], | ||||
| }; | ||||
|   | ||||
							
								
								
									
										41
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										41
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -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", | ||||
|   | ||||
							
								
								
									
										35
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								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", | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|           }} | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -12,7 +12,7 @@ export function FixedSideContainer({ | ||||
|   side, | ||||
| }: FixedSideContainerProps) { | ||||
|   return ( | ||||
|     <div className={"FixedSideContainer FixedSideContainer_side_" + side}> | ||||
|     <div className={`FixedSideContainer FixedSideContainer_side_${side}`}> | ||||
|       {children} | ||||
|     </div> | ||||
|   ); | ||||
|   | ||||
| @@ -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} | ||||
|   | ||||
| @@ -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(); | ||||
|   }; | ||||
|  | ||||
|   | ||||
| @@ -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}> | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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] && | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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,9 +1177,10 @@ 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) { | ||||
|                   const el = 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 || | ||||
|   | ||||
| @@ -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}`); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -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 | ||||
|   ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // This should be only called for exporting purposes | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Lipis
					Lipis