diff --git a/src/packages/frontend/frame-editors/whiteboard-editor/canvas.tsx b/src/packages/frontend/frame-editors/whiteboard-editor/canvas.tsx index 58a09cf0d9..625e98eb78 100644 --- a/src/packages/frontend/frame-editors/whiteboard-editor/canvas.tsx +++ b/src/packages/frontend/frame-editors/whiteboard-editor/canvas.tsx @@ -14,7 +14,7 @@ import { useRef, useState, } from "react"; -import { Element, Point } from "./types"; +import { Element, ElementType, Point } from "./types"; import { Tool, TOOLS } from "./tools/spec"; import RenderElement from "./elements/render"; import Focused, { SELECTED_BORDER_COLOR } from "./focused"; @@ -256,6 +256,8 @@ export default function Canvas({ key={id} canvasScale={canvasScale} element={element} + allElements={elements} + selectedElements={[element]} transforms={transforms} > {elt} @@ -289,6 +291,37 @@ export default function Canvas({ v.push(processElement(element)); } + if (selection != null && selection.size > 1) { + // create a virtual selection element that + // contains the region spanned by all elements + // in the selection. + // TODO: This could be optimized with better data structures... + const selectedElements = elements.filter((element) => + selection.has(element.id) + ); + const { xMin, yMin, xMax, yMax } = getPageSpan(selectedElements, 0); + const element = { + type: "selection" as ElementType, + id: "selection", + x: xMin, + y: yMin, + w: xMax - xMin + 1, + h: yMax - yMin + 1, + }; + v.push( + + + + ); + } + if (isNavigator) { // The navigator rectangle const visible = frame.desc.get("visibleWindow")?.toJS(); diff --git a/src/packages/frontend/frame-editors/whiteboard-editor/elements/render.tsx b/src/packages/frontend/frame-editors/whiteboard-editor/elements/render.tsx index a70ab70917..acd9c772da 100644 --- a/src/packages/frontend/frame-editors/whiteboard-editor/elements/render.tsx +++ b/src/packages/frontend/frame-editors/whiteboard-editor/elements/render.tsx @@ -10,6 +10,7 @@ import Frame from "./frame"; import Generic from "./generic"; import Pen from "./pen"; import Stopwatch from "./stopwatch"; +import Selection from "./selection"; interface Props { element: Element; @@ -33,6 +34,8 @@ export default function Render(props: Props) { return ; case "stopwatch": return ; + case "selection": + return ; default: return ; } diff --git a/src/packages/frontend/frame-editors/whiteboard-editor/elements/selection.tsx b/src/packages/frontend/frame-editors/whiteboard-editor/elements/selection.tsx new file mode 100644 index 0000000000..6cfd3c6272 --- /dev/null +++ b/src/packages/frontend/frame-editors/whiteboard-editor/elements/selection.tsx @@ -0,0 +1,13 @@ +import { SELECTED_BORDER_COLOR } from "../focused"; + +export default function Selection({ canvasScale }) { + return ( +
+ ); +} diff --git a/src/packages/frontend/frame-editors/whiteboard-editor/focused-resize.tsx b/src/packages/frontend/frame-editors/whiteboard-editor/focused-resize.tsx index e3409aa594..aafccf4fba 100644 --- a/src/packages/frontend/frame-editors/whiteboard-editor/focused-resize.tsx +++ b/src/packages/frontend/frame-editors/whiteboard-editor/focused-resize.tsx @@ -29,18 +29,25 @@ export default function DragHandle({ setOffset, canvasScale, element, + selectedElements, }: { top: boolean; left: boolean; setOffset: (offset: { x: number; y: number; w: number; h: number }) => void; canvasScale: number; element: Element; + selectedElements: Element[]; }) { const [position, setPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0, }); const frame = useFrameContext(); + + if (selectedElements.length > 1) { + return null; + } + const style = { pointerEvents: "all", // because we turn off pointer events for containing div cursor: dragHandleCursors[`${top}-${left}`], diff --git a/src/packages/frontend/frame-editors/whiteboard-editor/focused.tsx b/src/packages/frontend/frame-editors/whiteboard-editor/focused.tsx index f817cf6500..8238dd77df 100644 --- a/src/packages/frontend/frame-editors/whiteboard-editor/focused.tsx +++ b/src/packages/frontend/frame-editors/whiteboard-editor/focused.tsx @@ -37,6 +37,8 @@ interface Props { children: ReactNode; canvasScale: number; element: Element; + selectedElements: Element[]; + allElements: Element[]; transforms; } @@ -44,7 +46,9 @@ export default function Focused({ children, canvasScale, element, + selectedElements, transforms, + allElements, }: Props) { const frame = useFrameContext(); const rectRef = useRef(null); @@ -72,6 +76,7 @@ export default function Focused({ left={left} canvasScale={canvasScale} element={element} + selectedElements={selectedElements} setOffset={setOffset} /> ); @@ -120,6 +125,7 @@ export default function Focused({ } } setTimeout(() => { + if (id == "selection") return; // todo frame.actions.setElement({ id, rotate }); setRotating(undefined); }, 0); @@ -198,7 +204,7 @@ export default function Focused({ transformOrigin: "top left", }} > - + { setDragging(false); - const { id } = element; - const x = element.x + data.x; - const y = element.y + data.y; - frame.actions.setElement({ id, x, y }); + for (const elt of selectedElements) { + const { id } = elt; + const x = elt.x + data.x; + const y = elt.y + data.y; + frame.actions.setElement({ id, x, y }); + } }} >
- +
); @@ -225,13 +230,13 @@ function getFontFamily(elements: Element[]): string | undefined { return DEFAULT_FONT_FAMILY; } -function OtherOperations({ actions, elements }: ButtonProps) { +function OtherOperations({ actions, elements, allElements }) { const frame = useFrameContext(); const menu = ( { if (key == "bring-to-front") { - const { zMax } = getPageSpan(elements); + const { zMax } = getPageSpan(allElements); let z = zMax + 1; for (const element of elements) { actions.setElement({ ...element, z }, false); @@ -240,9 +245,8 @@ function OtherOperations({ actions, elements }: ButtonProps) { actions.syncstring_commit(); actions.clearSelection(frame.id); } else if (key == "send-to-back") { - const { zMin } = getPageSpan(elements); + const { zMin } = getPageSpan(allElements); let z = zMin - 1; - console.log("zMin = ", zMin, " z = ", z); for (const element of elements) { actions.setElement({ ...element, z }, false); z -= 1; diff --git a/src/packages/frontend/frame-editors/whiteboard-editor/types.ts b/src/packages/frontend/frame-editors/whiteboard-editor/types.ts index d9cd575d5b..62f8dc1ce4 100644 --- a/src/packages/frontend/frame-editors/whiteboard-editor/types.ts +++ b/src/packages/frontend/frame-editors/whiteboard-editor/types.ts @@ -17,7 +17,8 @@ export type ElementType = | "terminal" | "stopwatch" | "timer" - | "frame"; + | "frame" + | "selection"; export type Point = { x: number; y: number };