feat: Change grid size

change-grid
Panayiotis Lipiridis 4 years ago
parent 39e7b8cf4f
commit 40cd4caeec

@ -6,6 +6,7 @@ import {
DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE,
DEFAULT_FONT_FAMILY, DEFAULT_FONT_FAMILY,
DEFAULT_TEXT_ALIGN, DEFAULT_TEXT_ALIGN,
GRID_SIZE,
} from "./constants"; } from "./constants";
export const getDefaultAppState = (): Omit< export const getDefaultAppState = (): Omit<
@ -65,7 +66,8 @@ export const getDefaultAppState = (): Omit<
showShortcutsDialog: false, showShortcutsDialog: false,
suggestedBindings: [], suggestedBindings: [],
zenModeEnabled: false, zenModeEnabled: false,
gridSize: null, gridSize: GRID_SIZE,
showGrid: false,
editingGroupId: null, editingGroupId: null,
selectedGroupIds: {}, selectedGroupIds: {},
width: window.innerWidth, width: window.innerWidth,
@ -121,6 +123,7 @@ const APP_STATE_STORAGE_CONF = (<
exportBackground: { browser: true, export: false }, exportBackground: { browser: true, export: false },
exportEmbedScene: { browser: true, export: false }, exportEmbedScene: { browser: true, export: false },
gridSize: { browser: true, export: true }, gridSize: { browser: true, export: true },
showGrid: { browser: true, export: false },
height: { browser: false, export: false }, height: { browser: false, export: false },
isBindingEnabled: { browser: false, export: false }, isBindingEnabled: { browser: false, export: false },
isLibraryOpen: { browser: false, export: false }, isLibraryOpen: { browser: false, export: false },

@ -1036,7 +1036,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const dy = y - elementsCenterY; const dy = y - elementsCenterY;
const groupIdMap = new Map(); const groupIdMap = new Map();
const [gridX, gridY] = getGridPoint(dx, dy, this.state.gridSize); const [gridX, gridY] = getGridPoint(
dx,
dy,
this.state.showGrid,
this.state.gridSize,
);
const oldIdToDuplicatedId = new Map(); const oldIdToDuplicatedId = new Map();
const newElements = clipboardElements.map((element) => { const newElements = clipboardElements.map((element) => {
@ -1149,7 +1154,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
toggleGridMode = () => { toggleGridMode = () => {
this.setState({ this.setState({
gridSize: this.state.gridSize ? null : GRID_SIZE, showGrid: !this.state.showGrid,
}); });
}; };
@ -1275,7 +1280,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (isArrowKey(event.key)) { if (isArrowKey(event.key)) {
const step = const step =
(this.state.gridSize && (this.state.showGrid &&
(event.shiftKey ? ELEMENT_TRANSLATE_AMOUNT : this.state.gridSize)) || (event.shiftKey ? ELEMENT_TRANSLATE_AMOUNT : this.state.gridSize)) ||
(event.shiftKey (event.shiftKey
? ELEMENT_SHIFT_TRANSLATE_AMOUNT ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
@ -1819,6 +1824,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
scenePointerX, scenePointerX,
scenePointerY, scenePointerY,
this.state.editingLinearElement, this.state.editingLinearElement,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
if (editingLinearElement !== this.state.editingLinearElement) { if (editingLinearElement !== this.state.editingLinearElement) {
@ -2249,7 +2255,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
return { return {
origin, origin,
originInGrid: tupleToCoors( originInGrid: tupleToCoors(
getGridPoint(origin.x, origin.y, this.state.gridSize), getGridPoint(
origin.x,
origin.y,
this.state.showGrid,
this.state.gridSize,
),
), ),
scrollbars: isOverScrollBars( scrollbars: isOverScrollBars(
currentScrollBars, currentScrollBars,
@ -2607,7 +2618,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerDownState.origin.x, pointerDownState.origin.x,
pointerDownState.origin.y, pointerDownState.origin.y,
elementType === "draw" ? null : this.state.gridSize, elementType === "draw" ? false : this.state.showGrid,
this.state.gridSize,
); );
/* If arrow is pre-arrowheads, it will have undefined for both start and end arrowheads. /* If arrow is pre-arrowheads, it will have undefined for both start and end arrowheads.
@ -2669,6 +2681,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerDownState.origin.x, pointerDownState.origin.x,
pointerDownState.origin.y, pointerDownState.origin.y,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
const element = newElement({ const element = newElement({
@ -2758,6 +2771,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerCoords.x, pointerCoords.x,
pointerCoords.y, pointerCoords.y,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
@ -2830,6 +2844,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [dragX, dragY] = getGridPoint( const [dragX, dragY] = getGridPoint(
pointerCoords.x - pointerDownState.drag.offset.x, pointerCoords.x - pointerDownState.drag.offset.x,
pointerCoords.y - pointerDownState.drag.offset.y, pointerCoords.y - pointerDownState.drag.offset.y,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
@ -2882,6 +2897,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [originDragX, originDragY] = getGridPoint( const [originDragX, originDragY] = getGridPoint(
pointerDownState.origin.x - pointerDownState.drag.offset.x, pointerDownState.origin.x - pointerDownState.drag.offset.x,
pointerDownState.origin.y - pointerDownState.drag.offset.y, pointerDownState.origin.y - pointerDownState.drag.offset.y,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
mutateElement(duplicatedElement, { mutateElement(duplicatedElement, {
@ -3542,6 +3558,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [gridX, gridY] = getGridPoint( const [gridX, gridY] = getGridPoint(
pointerCoords.x, pointerCoords.x,
pointerCoords.y, pointerCoords.y,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
dragNewElement( dragNewElement(
@ -3580,6 +3597,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const [resizeX, resizeY] = getGridPoint( const [resizeX, resizeY] = getGridPoint(
pointerCoords.x - pointerDownState.resize.offset.x, pointerCoords.x - pointerDownState.resize.offset.x,
pointerCoords.y - pointerDownState.resize.offset.y, pointerCoords.y - pointerDownState.resize.offset.y,
this.state.showGrid,
this.state.gridSize, this.state.gridSize,
); );
if ( if (

@ -0,0 +1,4 @@
.input {
cursor: ew-resize;
user-select: none;
}

@ -0,0 +1,68 @@
import React, { CSSProperties, useEffect, useState } from "react";
import classes from "./SlidableInput.module.css";
import { throttle } from "./utils/throttle";
interface SlidableInputProps {
value: number;
prefix?: string;
suffix?: string;
minValue?: number;
maxValue?: number;
style?: CSSProperties;
onChange?: (value: number) => void;
}
export const SlidableInput: React.FC<SlidableInputProps> = ({
value,
style,
prefix,
suffix,
onChange,
minValue,
maxValue,
}) => {
const [isLocked, setIsLocked] = useState<boolean>(true);
const previousX = React.useRef(0);
useEffect(() => {
const onMouseMoveHandler = throttle((event: MouseEvent) => {
if (isLocked) return;
const nextX = event.screenX;
if (nextX === previousX.current) return;
const nextValue = value + (nextX > previousX.current ? 1 : -1);
onChange &&
nextValue <= (maxValue || Infinity) &&
nextValue >= (typeof minValue === "number" ? minValue : -Infinity) &&
onChange(nextValue);
previousX.current = nextX;
}, 250) as EventListenerOrEventListenerObject;
window.addEventListener("mousemove", onMouseMoveHandler);
return () => {
window.removeEventListener("mousemove", onMouseMoveHandler);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLocked, value]);
const onMouseDown = () => setIsLocked(false);
useEffect(() => {
const onMouseUp = () => setIsLocked(true);
window.addEventListener("mouseup", onMouseUp);
return () => {
window.removeEventListener("mouseup", onMouseUp);
};
}, []);
return (
<span className={classes.input} style={style} onMouseDown={onMouseDown}>
{prefix}
{value}
{suffix}
</span>
);
};

@ -12,6 +12,7 @@ import { AppState } from "../types";
import { debounce, nFormatter } from "../utils"; import { debounce, nFormatter } from "../utils";
import { close } from "./icons"; import { close } from "./icons";
import { Island } from "./Island"; import { Island } from "./Island";
import { SlidableInput } from "./SlidableInput";
import "./Stats.scss"; import "./Stats.scss";
type StorageSizes = { scene: number; total: number }; type StorageSizes = { scene: number; total: number };
@ -157,6 +158,19 @@ export const Stats = (props: {
</td> </td>
</tr> </tr>
)} )}
<tr>
<th colSpan={2}>{"Misc"}</th>
</tr>
<tr>
<td>{"Grid size"}</td>
<td>
<SlidableInput
value={props.appState.gridSize || 8}
minValue={8}
/>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</Island> </Island>

@ -0,0 +1,14 @@
export const throttle = (func: Function, limit: number): Function => {
let inThrottle: boolean;
return function (this: any): any {
const args = arguments;
const context = this;
if (!inThrottle) {
inThrottle = true;
func.apply(context, args);
setTimeout(() => (inThrottle = false), limit);
}
};
};

@ -102,6 +102,7 @@ export class LinearElementEditor {
element, element,
scenePointerX - editingLinearElement.pointerOffset.x, scenePointerX - editingLinearElement.pointerOffset.x,
scenePointerY - editingLinearElement.pointerOffset.y, scenePointerY - editingLinearElement.pointerOffset.y,
appState.showGrid,
appState.gridSize, appState.gridSize,
); );
LinearElementEditor.movePoint(element, activePointIndex, newPoint); LinearElementEditor.movePoint(element, activePointIndex, newPoint);
@ -198,6 +199,7 @@ export class LinearElementEditor {
element, element,
scenePointer.x, scenePointer.x,
scenePointer.y, scenePointer.y,
appState.showGrid,
appState.gridSize, appState.gridSize,
), ),
], ],
@ -282,7 +284,8 @@ export class LinearElementEditor {
scenePointerX: number, scenePointerX: number,
scenePointerY: number, scenePointerY: number,
editingLinearElement: LinearElementEditor, editingLinearElement: LinearElementEditor,
gridSize: number | null, isGridOn: boolean,
gridSize: number,
): LinearElementEditor { ): LinearElementEditor {
const { elementId, lastUncommittedPoint } = editingLinearElement; const { elementId, lastUncommittedPoint } = editingLinearElement;
const element = LinearElementEditor.getElement(elementId); const element = LinearElementEditor.getElement(elementId);
@ -304,6 +307,7 @@ export class LinearElementEditor {
element, element,
scenePointerX - editingLinearElement.pointerOffset.x, scenePointerX - editingLinearElement.pointerOffset.x,
scenePointerY - editingLinearElement.pointerOffset.y, scenePointerY - editingLinearElement.pointerOffset.y,
isGridOn,
gridSize, gridSize,
); );
@ -398,9 +402,15 @@ export class LinearElementEditor {
element: NonDeleted<ExcalidrawLinearElement>, element: NonDeleted<ExcalidrawLinearElement>,
scenePointerX: number, scenePointerX: number,
scenePointerY: number, scenePointerY: number,
gridSize: number | null, isGridOn: boolean,
gridSize: number,
): Point { ): Point {
const pointerOnGrid = getGridPoint(scenePointerX, scenePointerY, gridSize); const pointerOnGrid = getGridPoint(
scenePointerX,
scenePointerY,
isGridOn,
gridSize,
);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const cx = (x1 + x2) / 2; const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2; const cy = (y1 + y2) / 2;

@ -307,9 +307,10 @@ const doSegmentsIntersect = (p1: Point, q1: Point, p2: Point, q2: Point) => {
export const getGridPoint = ( export const getGridPoint = (
x: number, x: number,
y: number, y: number,
gridSize: number | null, isGridOn: boolean,
gridSize: number,
): [number, number] => { ): [number, number] => {
if (gridSize) { if (isGridOn) {
return [ return [
Math.round(x / gridSize) * gridSize, Math.round(x / gridSize) * gridSize,
Math.round(y / gridSize) * gridSize, Math.round(y / gridSize) * gridSize,

@ -75,7 +75,7 @@ const excalidrawDiagram = {
], ],
appState: { appState: {
viewBackgroundColor: "#ffffff", viewBackgroundColor: "#ffffff",
gridSize: null, gridSize: 20,
}, },
}; };

@ -83,7 +83,8 @@ export type AppState = {
showShortcutsDialog: boolean; showShortcutsDialog: boolean;
zenModeEnabled: boolean; zenModeEnabled: boolean;
appearance: "light" | "dark"; appearance: "light" | "dark";
gridSize: number | null; gridSize: number;
showGrid: boolean;
/** top-most selected groups (i.e. does not include nested groups) */ /** top-most selected groups (i.e. does not include nested groups) */
selectedGroupIds: { [groupId: string]: boolean }; selectedGroupIds: { [groupId: string]: boolean };

Loading…
Cancel
Save