From c6a27e7212c4998ec8e7f658dab3f0839a748058 Mon Sep 17 00:00:00 2001 From: Guillermo Peralta Scura Date: Wed, 8 Jan 2020 22:26:24 -0300 Subject: [PATCH] Show resize handlers on hover --- src/element/handlerRectangles.ts | 36 ++++++++++---------- src/element/resizeTest.ts | 31 +++++++++++++---- src/index.tsx | 57 +++++++++++++++++--------------- src/scene/types.ts | 5 +++ 4 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/element/handlerRectangles.ts b/src/element/handlerRectangles.ts index 6e01c9d86..985751109 100644 --- a/src/element/handlerRectangles.ts +++ b/src/element/handlerRectangles.ts @@ -1,11 +1,11 @@ -import { SceneState } from "../scene/types"; import { ExcalidrawElement } from "./types"; +import { SceneScroll } from "../scene/types"; type Sides = "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se"; export function handlerRectangles( element: ExcalidrawElement, - sceneState: SceneState + { scrollX, scrollY }: SceneScroll ) { const elementX1 = element.x; const elementX2 = element.x + element.width; @@ -21,15 +21,15 @@ export function handlerRectangles( if (Math.abs(elementX2 - elementX1) > minimumSize) { handlers["n"] = [ - elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4, - elementY1 - margin + sceneState.scrollY + marginY, + elementX1 + (elementX2 - elementX1) / 2 + scrollX - 4, + elementY1 - margin + scrollY + marginY, 8, 8 ]; handlers["s"] = [ - elementX1 + (elementX2 - elementX1) / 2 + sceneState.scrollX - 4, - elementY2 - margin + sceneState.scrollY - marginY, + elementX1 + (elementX2 - elementX1) / 2 + scrollX - 4, + elementY2 - margin + scrollY - marginY, 8, 8 ]; @@ -37,41 +37,41 @@ export function handlerRectangles( if (Math.abs(elementY2 - elementY1) > minimumSize) { handlers["w"] = [ - elementX1 - margin + sceneState.scrollX + marginX, - elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4, + elementX1 - margin + scrollX + marginX, + elementY1 + (elementY2 - elementY1) / 2 + scrollY - 4, 8, 8 ]; handlers["e"] = [ - elementX2 - margin + sceneState.scrollX - marginX, - elementY1 + (elementY2 - elementY1) / 2 + sceneState.scrollY - 4, + elementX2 - margin + scrollX - marginX, + elementY1 + (elementY2 - elementY1) / 2 + scrollY - 4, 8, 8 ]; } handlers["nw"] = [ - elementX1 - margin + sceneState.scrollX + marginX, - elementY1 - margin + sceneState.scrollY + marginY, + elementX1 - margin + scrollX + marginX, + elementY1 - margin + scrollY + marginY, 8, 8 ]; // nw handlers["ne"] = [ - elementX2 - margin + sceneState.scrollX - marginX, - elementY1 - margin + sceneState.scrollY + marginY, + elementX2 - margin + scrollX - marginX, + elementY1 - margin + scrollY + marginY, 8, 8 ]; // ne handlers["sw"] = [ - elementX1 - margin + sceneState.scrollX + marginX, - elementY2 - margin + sceneState.scrollY - marginY, + elementX1 - margin + scrollX + marginX, + elementY2 - margin + scrollY - marginY, 8, 8 ]; // sw handlers["se"] = [ - elementX2 - margin + sceneState.scrollX - marginX, - elementY2 - margin + sceneState.scrollY - marginY, + elementX2 - margin + scrollX - marginX, + elementY2 - margin + scrollY - marginY, 8, 8 ]; // se diff --git a/src/element/resizeTest.ts b/src/element/resizeTest.ts index a74a2feca..51ecf7ef8 100644 --- a/src/element/resizeTest.ts +++ b/src/element/resizeTest.ts @@ -1,7 +1,7 @@ import { ExcalidrawElement } from "./types"; -import { SceneState } from "../scene/types"; import { handlerRectangles } from "./handlerRectangles"; +import { SceneScroll } from "../scene/types"; type HandlerRectanglesRet = keyof ReturnType; @@ -9,20 +9,20 @@ export function resizeTest( element: ExcalidrawElement, x: number, y: number, - sceneState: SceneState + { scrollX, scrollY }: SceneScroll ): HandlerRectanglesRet | false { if (!element.isSelected || element.type === "text") return false; - const handlers = handlerRectangles(element, sceneState); + const handlers = handlerRectangles(element, { scrollX, scrollY }); const filter = Object.keys(handlers).filter(key => { const handler = handlers[key as HandlerRectanglesRet]!; return ( - x + sceneState.scrollX >= handler[0] && - x + sceneState.scrollX <= handler[0] + handler[2] && - y + sceneState.scrollY >= handler[1] && - y + sceneState.scrollY <= handler[1] + handler[3] + x + scrollX >= handler[0] && + x + scrollX <= handler[0] + handler[2] && + y + scrollY >= handler[1] && + y + scrollY <= handler[1] + handler[3] ); }); @@ -32,3 +32,20 @@ export function resizeTest( return false; } + +export function getElementWithResizeHandler( + elements: ExcalidrawElement[], + { x, y }: { x: number; y: number }, + { scrollX, scrollY }: SceneScroll +) { + return elements.reduce((result, element) => { + if (result) { + return result; + } + const resizeHandle = resizeTest(element, x, y, { + scrollX, + scrollY + }); + return resizeHandle ? { element, resizeHandle } : null; + }, null as { element: ExcalidrawElement; resizeHandle: ReturnType } | null); +} diff --git a/src/index.tsx b/src/index.tsx index 9297de568..350debdbb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -52,6 +52,7 @@ import { PanelCanvas } from "./components/panels/PanelCanvas"; import { Panel } from "./components/Panel"; import "./styles.scss"; +import { getElementWithResizeHandler } from "./element/resizeTest"; const { elements } = createScene(); const { history } = createHistory(); @@ -541,9 +542,7 @@ export class App extends React.Component<{}, AppState> { onContextMenu={e => { e.preventDefault(); - const x = - e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX; - const y = e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY; + const { x, y } = viewportCoordsToSceneCoords(e, this.state); const element = getElementAtPosition(elements, x, y); if (!element) { @@ -633,28 +632,23 @@ export class App extends React.Component<{}, AppState> { 1, 100 ); - let resizeHandle: ReturnType = false; + type ResizeTestType = ReturnType; + let resizeHandle: ResizeTestType = false; let isDraggingElements = false; let isResizingElements = false; if (this.state.elementType === "selection") { - const resizeElement = elements.find(element => { - return resizeTest(element, x, y, { - scrollX: this.state.scrollX, - scrollY: this.state.scrollY, - viewBackgroundColor: this.state.viewBackgroundColor - }); - }); + const resizeElement = getElementWithResizeHandler( + elements, + { x, y }, + this.state + ); this.setState({ - resizingElement: resizeElement ? resizeElement : null + resizingElement: resizeElement ? resizeElement.element : null }); if (resizeElement) { - resizeHandle = resizeTest(resizeElement, x, y, { - scrollX: this.state.scrollX, - scrollY: this.state.scrollY, - viewBackgroundColor: this.state.viewBackgroundColor - }); + resizeHandle = resizeElement.resizeHandle; document.documentElement.style.cursor = `${resizeHandle}-resize`; isResizingElements = true; } else { @@ -778,10 +772,8 @@ export class App extends React.Component<{}, AppState> { const el = this.state.resizingElement; const selectedElements = elements.filter(el => el.isSelected); if (selectedElements.length === 1) { - const x = - e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX; - const y = - e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY; + const { x, y } = viewportCoordsToSceneCoords(e, this.state); + selectedElements.forEach(element => { switch (resizeHandle) { case "nw": @@ -853,10 +845,8 @@ export class App extends React.Component<{}, AppState> { if (isDraggingElements) { const selectedElements = elements.filter(el => el.isSelected); if (selectedElements.length) { - const x = - e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX; - const y = - e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY; + const { x, y } = viewportCoordsToSceneCoords(e, this.state); + selectedElements.forEach(element => { element.x += x - lastX; element.y += y - lastY; @@ -1020,9 +1010,24 @@ export class App extends React.Component<{}, AppState> { return; } const { x, y } = viewportCoordsToSceneCoords(e, this.state); + const resizeElement = getElementWithResizeHandler( + elements, + { x, y }, + this.state + ); + if (resizeElement && resizeElement.resizeHandle) { + document.documentElement.style.cursor = `${resizeElement.resizeHandle}-resize`; + return; + } const hitElement = getElementAtPosition(elements, x, y); if (hitElement) { - document.documentElement.style.cursor = `move`; + const resizeHandle = resizeTest(hitElement, x, y, { + scrollX: this.state.scrollX, + scrollY: this.state.scrollY + }); + document.documentElement.style.cursor = resizeHandle + ? `${resizeHandle}-resize` + : `move`; } else { document.documentElement.style.cursor = ``; } diff --git a/src/scene/types.ts b/src/scene/types.ts index 41a2f1b2a..c8904dca5 100644 --- a/src/scene/types.ts +++ b/src/scene/types.ts @@ -7,6 +7,11 @@ export type SceneState = { viewBackgroundColor: string | null; }; +export type SceneScroll = { + scrollX: number; + scrollY: number; +}; + export interface Scene { elements: ExcalidrawTextElement[]; }