From 61e5b66dac00176b8f66ad6ba87160b35fc66404 Mon Sep 17 00:00:00 2001 From: Pete Hunt Date: Tue, 26 May 2020 13:07:46 -0700 Subject: [PATCH] Group/ungroup (#1648) Co-authored-by: dwelle --- src/actions/actionDuplicateSelection.tsx | 14 +- src/actions/actionGroup.ts | 119 ++++++ src/actions/actionSelectAll.ts | 24 +- src/actions/index.ts | 2 + src/actions/types.ts | 4 +- src/appState.ts | 2 + src/components/App.tsx | 163 ++++++-- src/components/ShortcutsDialog.tsx | 8 + src/data/restore.ts | 4 +- src/element/newElement.test.ts | 4 +- src/element/newElement.ts | 30 ++ src/element/types.ts | 6 + src/groups.ts | 130 ++++++ src/keys.ts | 1 + src/locales/en.json | 4 +- src/renderer/renderScene.ts | 130 ++++-- .../__snapshots__/dragCreate.test.tsx.snap | 5 + src/tests/__snapshots__/move.test.tsx.snap | 3 + .../multiPointCreate.test.tsx.snap | 2 + .../regressionTests.test.tsx.snap | 383 ++++++++++++++++++ src/tests/__snapshots__/resize.test.tsx.snap | 2 + .../__snapshots__/selection.test.tsx.snap | 5 + src/types.ts | 5 + 23 files changed, 964 insertions(+), 86 deletions(-) create mode 100644 src/actions/actionGroup.ts create mode 100644 src/groups.ts diff --git a/src/actions/actionDuplicateSelection.tsx b/src/actions/actionDuplicateSelection.tsx index 2723214d1..c0b157641 100644 --- a/src/actions/actionDuplicateSelection.tsx +++ b/src/actions/actionDuplicateSelection.tsx @@ -12,15 +12,21 @@ import { getShortcutKey } from "../utils"; export const actionDuplicateSelection = register({ name: "duplicateSelection", perform: (elements, appState) => { + const groupIdMap = new Map(); return { appState, elements: elements.reduce( (acc: Array, element: ExcalidrawElement) => { if (appState.selectedElementIds[element.id]) { - const newElement = duplicateElement(element, { - x: element.x + 10, - y: element.y + 10, - }); + const newElement = duplicateElement( + appState.editingGroupId, + groupIdMap, + element, + { + x: element.x + 10, + y: element.y + 10, + }, + ); appState.selectedElementIds[newElement.id] = true; delete appState.selectedElementIds[element.id]; return acc.concat([element, newElement]); diff --git a/src/actions/actionGroup.ts b/src/actions/actionGroup.ts new file mode 100644 index 000000000..4f580c636 --- /dev/null +++ b/src/actions/actionGroup.ts @@ -0,0 +1,119 @@ +import { KEYS } from "../keys"; +import { register } from "./register"; +import nanoid from "nanoid"; +import { newElementWith } from "../element/mutateElement"; +import { getSelectedElements } from "../scene"; +import { + getSelectedGroupIds, + selectGroup, + selectGroupsForSelectedElements, + getElementsInGroup, + addToGroup, + removeFromSelectedGroups, +} from "../groups"; +import { getNonDeletedElements } from "../element"; + +export const actionGroup = register({ + name: "group", + perform: (elements, appState) => { + const selectedElements = getSelectedElements( + getNonDeletedElements(elements), + appState, + ); + if (selectedElements.length < 2) { + // nothing to group + return { appState, elements, commitToHistory: false }; + } + // if everything is already grouped into 1 group, there is nothing to do + const selectedGroupIds = getSelectedGroupIds(appState); + if (selectedGroupIds.length === 1) { + const selectedGroupId = selectedGroupIds[0]; + const elementIdsInGroup = new Set( + getElementsInGroup(elements, selectedGroupId).map( + (element) => element.id, + ), + ); + const selectedElementIds = new Set( + selectedElements.map((element) => element.id), + ); + const combinedSet = new Set([ + ...Array.from(elementIdsInGroup), + ...Array.from(selectedElementIds), + ]); + if (combinedSet.size === elementIdsInGroup.size) { + // no incremental ids in the selected ids + return { appState, elements, commitToHistory: false }; + } + } + const newGroupId = nanoid(); + const updatedElements = elements.map((element) => { + if (!appState.selectedElementIds[element.id]) { + return element; + } + return newElementWith(element, { + groupIds: addToGroup( + element.groupIds, + newGroupId, + appState.editingGroupId, + ), + }); + }); + return { + appState: selectGroup( + newGroupId, + { ...appState, selectedGroupIds: {} }, + getNonDeletedElements(updatedElements), + ), + elements: updatedElements, + commitToHistory: true, + }; + }, + contextMenuOrder: 4, + contextItemLabel: "labels.group", + keyTest: (event) => { + return ( + !event.shiftKey && + event[KEYS.CTRL_OR_CMD] && + event.keyCode === KEYS.G_KEY_CODE + ); + }, +}); + +export const actionUngroup = register({ + name: "ungroup", + perform: (elements, appState) => { + const groupIds = getSelectedGroupIds(appState); + if (groupIds.length === 0) { + return { appState, elements, commitToHistory: false }; + } + const nextElements = elements.map((element) => { + const nextGroupIds = removeFromSelectedGroups( + element.groupIds, + appState.selectedGroupIds, + ); + if (nextGroupIds.length === element.groupIds.length) { + return element; + } + return newElementWith(element, { + groupIds: nextGroupIds, + }); + }); + return { + appState: selectGroupsForSelectedElements( + { ...appState, selectedGroupIds: {} }, + getNonDeletedElements(nextElements), + ), + elements: nextElements, + commitToHistory: true, + }; + }, + keyTest: (event) => { + return ( + event.shiftKey && + event[KEYS.CTRL_OR_CMD] && + event.keyCode === KEYS.G_KEY_CODE + ); + }, + contextMenuOrder: 5, + contextItemLabel: "labels.ungroup", +}); diff --git a/src/actions/actionSelectAll.ts b/src/actions/actionSelectAll.ts index c996b4316..81d775e66 100644 --- a/src/actions/actionSelectAll.ts +++ b/src/actions/actionSelectAll.ts @@ -1,19 +1,25 @@ import { KEYS } from "../keys"; import { register } from "./register"; +import { selectGroupsForSelectedElements } from "../groups"; +import { getNonDeletedElements } from "../element"; export const actionSelectAll = register({ name: "selectAll", perform: (elements, appState) => { return { - appState: { - ...appState, - selectedElementIds: elements.reduce((map, element) => { - if (!element.isDeleted) { - map[element.id] = true; - } - return map; - }, {} as any), - }, + appState: selectGroupsForSelectedElements( + { + ...appState, + editingGroupId: null, + selectedElementIds: elements.reduce((map, element) => { + if (!element.isDeleted) { + map[element.id] = true; + } + return map; + }, {} as any), + }, + getNonDeletedElements(elements), + ), commitToHistory: true, }; }, diff --git a/src/actions/index.ts b/src/actions/index.ts index 885ef67e5..804455693 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -44,3 +44,5 @@ export { actionFullScreen, actionShortcuts, } from "./actionMenu"; + +export { actionGroup, actionUngroup } from "./actionGroup"; diff --git a/src/actions/types.ts b/src/actions/types.ts index 1f94f1727..c803038f7 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -55,7 +55,9 @@ export type ActionName = | "changeFontFamily" | "changeTextAlign" | "toggleFullScreen" - | "toggleShortcuts"; + | "toggleShortcuts" + | "group" + | "ungroup"; export interface Action { name: ActionName; diff --git a/src/appState.ts b/src/appState.ts index eb6af942c..d8a9c26e8 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -48,6 +48,8 @@ export const getDefaultAppState = (): AppState => { shouldCacheIgnoreZoom: false, showShortcutsDialog: false, zenModeEnabled: false, + editingGroupId: null, + selectedGroupIds: {}, }; }; diff --git a/src/components/App.tsx b/src/components/App.tsx index c375a770c..7d50315fe 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -131,6 +131,12 @@ import { } from "../data/localStorage"; import throttle from "lodash.throttle"; +import { + getSelectedGroupIds, + selectGroupsForSelectedElements, + isElementInGroup, + getSelectedGroupIdForElement, +} from "../groups"; /** * @param func handler taking at most single parameter (event). @@ -704,9 +710,10 @@ class App extends React.Component { const dx = x - elementsCenterX; const dy = y - elementsCenterY; + const groupIdMap = new Map(); const newElements = clipboardElements.map((element) => - duplicateElement(element, { + duplicateElement(this.state.editingGroupId, groupIdMap, element, { x: element.x + dx - minX, y: element.y + dy - minY, }), @@ -1212,7 +1219,11 @@ class App extends React.Component { resetCursor(); } else { setCursorForShape(this.state.elementType); - this.setState({ selectedElementIds: {} }); + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); } isHoldingSpace = false; } @@ -1226,7 +1237,12 @@ class App extends React.Component { document.activeElement.blur(); } if (elementType !== "selection") { - this.setState({ elementType, selectedElementIds: {} }); + this.setState({ + elementType, + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); } else { this.setState({ elementType }); } @@ -1337,7 +1353,11 @@ class App extends React.Component { }), }); // deselect all other elements when inserting text - this.setState({ selectedElementIds: {} }); + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); // do an initial update to re-initialize element position since we were // modifying element's x/y for sake of editor (case: syncing to remote) @@ -1459,8 +1479,6 @@ class App extends React.Component { return; } - resetCursor(); - const { x, y } = viewportCoordsToSceneCoords( event, this.state, @@ -1468,6 +1486,40 @@ class App extends React.Component { window.devicePixelRatio, ); + const selectedGroupIds = getSelectedGroupIds(this.state); + + if (selectedGroupIds.length > 0) { + const elements = globalSceneState.getElements(); + const hitElement = getElementAtPosition( + elements, + this.state, + x, + y, + this.state.zoom, + ); + + const selectedGroupId = + hitElement && + getSelectedGroupIdForElement(hitElement, this.state.selectedGroupIds); + + if (selectedGroupId) { + this.setState((prevState) => + selectGroupsForSelectedElements( + { + ...prevState, + editingGroupId: selectedGroupId, + selectedElementIds: { [hitElement!.id]: true }, + selectedGroupIds: {}, + }, + globalSceneState.getElements(), + ), + ); + return; + } + } + + resetCursor(); + this.startTextEditing({ x: x, y: y, @@ -1942,7 +1994,16 @@ class App extends React.Component { !(hitElement && this.state.selectedElementIds[hitElement.id]) && !event.shiftKey ) { - this.setState({ selectedElementIds: {} }); + this.setState((prevState) => ({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: + prevState.editingGroupId && + hitElement && + isElementInGroup(hitElement, prevState.editingGroupId) + ? prevState.editingGroupId + : null, + })); } // If we click on something @@ -1952,12 +2013,32 @@ class App extends React.Component { // otherwise, it will trigger selection based on current // state of the box if (!this.state.selectedElementIds[hitElement.id]) { - this.setState((prevState) => ({ - selectedElementIds: { - ...prevState.selectedElementIds, - [hitElement!.id]: true, - }, - })); + // if we are currently editing a group, treat all selections outside of the group + // as exiting editing mode. + if ( + this.state.editingGroupId && + !isElementInGroup(hitElement, this.state.editingGroupId) + ) { + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); + return; + } + this.setState((prevState) => { + return selectGroupsForSelectedElements( + { + ...prevState, + selectedElementIds: { + ...prevState.selectedElementIds, + [hitElement!.id]: true, + }, + }, + globalSceneState.getElements(), + ); + }); + // TODO: this is strange... globalSceneState.replaceAllElements( globalSceneState.getElementsIncludingDeleted(), ); @@ -1966,7 +2047,11 @@ class App extends React.Component { } } } else { - this.setState({ selectedElementIds: {} }); + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); } if (this.state.elementType === "text") { @@ -2218,6 +2303,7 @@ class App extends React.Component { const nextElements = []; const elementsToAppend = []; + const groupIdMap = new Map(); for (const element of globalSceneState.getElementsIncludingDeleted()) { if ( this.state.selectedElementIds[element.id] || @@ -2225,7 +2311,11 @@ class App extends React.Component { // updated yet by the time this mousemove event is fired (element.id === hitElement.id && hitElementWasAddedToSelection) ) { - const duplicatedElement = duplicateElement(element); + const duplicatedElement = duplicateElement( + this.state.editingGroupId, + groupIdMap, + element, + ); mutateElement(duplicatedElement, { x: duplicatedElement.x + (originX - lastX), y: duplicatedElement.y + (originY - lastY), @@ -2316,21 +2406,31 @@ class App extends React.Component { if (this.state.elementType === "selection") { const elements = globalSceneState.getElements(); if (!event.shiftKey && isSomeElementSelected(elements, this.state)) { - this.setState({ selectedElementIds: {} }); + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); } const elementsWithinSelection = getElementsWithinSelection( elements, draggingElement, ); - this.setState((prevState) => ({ - selectedElementIds: { - ...prevState.selectedElementIds, - ...elementsWithinSelection.reduce((map, element) => { - map[element.id] = true; - return map; - }, {} as any), - }, - })); + this.setState((prevState) => + selectGroupsForSelectedElements( + { + ...prevState, + selectedElementIds: { + ...prevState.selectedElementIds, + ...elementsWithinSelection.reduce((map, element) => { + map[element.id] = true; + return map; + }, {} as any), + }, + }, + globalSceneState.getElements(), + ), + ); } }); @@ -2445,7 +2545,12 @@ class App extends React.Component { // If click occurred and elements were dragged or some element // was added to selection (on pointerdown phase) we need to keep // selection unchanged - if (hitElement && !draggingOccurred && !hitElementWasAddedToSelection) { + if ( + getSelectedGroupIds(this.state).length === 0 && + hitElement && + !draggingOccurred && + !hitElementWasAddedToSelection + ) { if (childEvent.shiftKey) { this.setState((prevState) => ({ selectedElementIds: { @@ -2462,7 +2567,11 @@ class App extends React.Component { if (draggingElement === null) { // if no element is clicked, clear the selection and redraw - this.setState({ selectedElementIds: {} }); + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + editingGroupId: null, + }); return; } diff --git a/src/components/ShortcutsDialog.tsx b/src/components/ShortcutsDialog.tsx index 61a3fe358..decd53c08 100644 --- a/src/components/ShortcutsDialog.tsx +++ b/src/components/ShortcutsDialog.tsx @@ -318,6 +318,14 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => { label={t("buttons.redo")} shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Z")]} /> + + diff --git a/src/data/restore.ts b/src/data/restore.ts index d174565a8..afd98906e 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -71,7 +71,8 @@ export const restore = ( return { ...element, - // all elements must have version > 0 so getDrawingVersion() will pick up newly added elements + // all elements must have version > 0 so getDrawingVersion() will pick + // up newly added elements version: element.version || 1, id: element.id || randomId(), isDeleted: false, @@ -84,6 +85,7 @@ export const restore = ( ? 100 : element.opacity, angle: element.angle ?? 0, + groupIds: element.groupIds || [], }; }); diff --git a/src/element/newElement.test.ts b/src/element/newElement.test.ts index 51b798369..1199c5dfe 100644 --- a/src/element/newElement.test.ts +++ b/src/element/newElement.test.ts @@ -45,7 +45,7 @@ it("clones arrow element", () => { ], }); - const copy = duplicateElement(element); + const copy = duplicateElement(null, new Map(), element); assertCloneObjects(element, copy); @@ -82,7 +82,7 @@ it("clones text element", () => { textAlign: "left", }); - const copy = duplicateElement(element); + const copy = duplicateElement(null, new Map(), element); assertCloneObjects(element, copy); diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 8c4670052..225650687 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -5,10 +5,13 @@ import { ExcalidrawGenericElement, NonDeleted, TextAlign, + GroupId, } from "../element/types"; import { measureText } from "../utils"; import { randomInteger, randomId } from "../random"; import { newElementWith } from "./mutateElement"; +import nanoid from "nanoid"; +import { getNewGroupIdsForDuplication } from "../groups"; type ElementConstructorOpts = { x: ExcalidrawGenericElement["x"]; @@ -61,6 +64,7 @@ const _newElementBase = ( version: rest.version || 1, versionNonce: rest.versionNonce ?? 0, isDeleted: false as false, + groupIds: [], }); export const newElement = ( @@ -148,13 +152,39 @@ export const deepCopyElement = (val: any, depth: number = 0) => { return val; }; +/** + * Duplicate an element, often used in the alt-drag operation. + * Note that this method has gotten a bit complicated since the + * introduction of gruoping/ungrouping elements. + * @param editingGroupId The current group being edited. The new + * element will inherit this group and its + * parents. + * @param groupIdMapForOperation A Map that maps old group IDs to + * duplicated ones. If you are duplicating + * multiple elements at once, share this map + * amongst all of them + * @param element Element to duplicate + * @param overrides Any element properties to override + */ export const duplicateElement = >( + editingGroupId: GroupId | null, + groupIdMapForOperation: Map, element: TElement, overrides?: Partial, ): TElement => { let copy: TElement = deepCopyElement(element); copy.id = randomId(); copy.seed = randomInteger(); + copy.groupIds = getNewGroupIdsForDuplication( + copy.groupIds, + editingGroupId, + (groupId) => { + if (!groupIdMapForOperation.has(groupId)) { + groupIdMapForOperation.set(groupId, nanoid()); + } + return groupIdMapForOperation.get(groupId)!; + }, + ); if (overrides) { copy = Object.assign(copy, overrides); } diff --git a/src/element/types.ts b/src/element/types.ts index e4c8383c7..08a42418f 100644 --- a/src/element/types.ts +++ b/src/element/types.ts @@ -1,5 +1,7 @@ import { Point } from "../types"; +export type GroupId = string; + type _ExcalidrawElementBase = Readonly<{ id: string; x: number; @@ -18,8 +20,12 @@ type _ExcalidrawElementBase = Readonly<{ version: number; versionNonce: number; isDeleted: boolean; + groupIds: GroupId[]; }>; +/** + * These are elements that don't have any additional properties. + */ export type ExcalidrawGenericElement = _ExcalidrawElementBase & { type: "selection" | "rectangle" | "diamond" | "ellipse"; }; diff --git a/src/groups.ts b/src/groups.ts new file mode 100644 index 000000000..b45961a81 --- /dev/null +++ b/src/groups.ts @@ -0,0 +1,130 @@ +import { GroupId, ExcalidrawElement, NonDeleted } from "./element/types"; +import { AppState } from "./types"; +import { getSelectedElements } from "./scene"; + +export function selectGroup( + groupId: GroupId, + appState: AppState, + elements: readonly NonDeleted[], +): AppState { + return { + ...appState, + selectedGroupIds: { ...appState.selectedGroupIds, [groupId]: true }, + selectedElementIds: { + ...appState.selectedElementIds, + ...Object.fromEntries( + elements + .filter((element) => element.groupIds.includes(groupId)) + .map((element) => [element.id, true]), + ), + }, + }; +} + +/** + * If the element's group is selected, don't render an individual + * selection border around it. + */ +export function isSelectedViaGroup( + appState: AppState, + element: ExcalidrawElement, +) { + return !!element.groupIds + .filter((groupId) => groupId !== appState.editingGroupId) + .find((groupId) => appState.selectedGroupIds[groupId]); +} + +export function getSelectedGroupIds(appState: AppState): GroupId[] { + return Object.entries(appState.selectedGroupIds) + .filter(([groupId, isSelected]) => isSelected) + .map(([groupId, isSelected]) => groupId); +} + +/** + * When you select an element, you often want to actually select the whole group it's in, unless + * you're currently editing that group. + */ +export function selectGroupsForSelectedElements( + appState: AppState, + elements: readonly NonDeleted[], +): AppState { + let nextAppState = { ...appState }; + + const selectedElements = getSelectedElements(elements, appState); + + for (const selectedElement of selectedElements) { + let groupIds = selectedElement.groupIds; + if (appState.editingGroupId) { + // handle the case where a group is nested within a group + const indexOfEditingGroup = groupIds.indexOf(appState.editingGroupId); + if (indexOfEditingGroup > -1) { + groupIds = groupIds.slice(0, indexOfEditingGroup); + } + } + if (groupIds.length > 0) { + const groupId = groupIds[groupIds.length - 1]; + nextAppState = selectGroup(groupId, nextAppState, elements); + } + } + + return nextAppState; +} + +export function isElementInGroup(element: ExcalidrawElement, groupId: string) { + return element.groupIds.includes(groupId); +} + +export function getElementsInGroup( + elements: readonly ExcalidrawElement[], + groupId: string, +) { + return elements.filter((element) => isElementInGroup(element, groupId)); +} + +export function getSelectedGroupIdForElement( + element: ExcalidrawElement, + selectedGroupIds: { [groupId: string]: boolean }, +) { + return element.groupIds.find((groupId) => selectedGroupIds[groupId]); +} + +export function getNewGroupIdsForDuplication( + groupIds: GroupId[], + editingGroupId: GroupId | null, + mapper: (groupId: GroupId) => GroupId, +) { + const copy = [...groupIds]; + const positionOfEditingGroupId = editingGroupId + ? groupIds.indexOf(editingGroupId) + : -1; + const endIndex = + positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length; + for (let i = 0; i < endIndex; i++) { + copy[i] = mapper(copy[i]); + } + + return copy; +} + +export function addToGroup( + prevGroupIds: GroupId[], + newGroupId: GroupId, + editingGroupId: GroupId | null, +) { + // insert before the editingGroupId, or push to the end. + const groupIds = [...prevGroupIds]; + const positionOfEditingGroupId = editingGroupId + ? groupIds.indexOf(editingGroupId) + : -1; + const positionToInsert = + positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length; + groupIds.splice(positionToInsert, 0, newGroupId); + return groupIds; +} + +export function removeFromSelectedGroups( + groupIds: GroupId[], + selectedGroupIds: { [groupId: string]: boolean }, +) { + return groupIds.filter((groupId) => !selectedGroupIds[groupId]); +} diff --git a/src/keys.ts b/src/keys.ts index 64d2eb5d3..3b181ad14 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -16,6 +16,7 @@ export const KEYS = { F_KEY_CODE: 70, ALT_KEY_CODE: 18, Z_KEY_CODE: 90, + G_KEY_CODE: 71, } as const; export type Key = keyof typeof KEYS; diff --git a/src/locales/en.json b/src/locales/en.json index 910d68247..c26bba9c8 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -59,7 +59,9 @@ "untitled": "Untitled", "name": "Name", "yourName": "Your name", - "madeWithExcalidraw": "Made with Excalidraw" + "madeWithExcalidraw": "Made with Excalidraw", + "group": "Group selection", + "ungroup": "Ungroup selection" }, "buttons": { "clearReset": "Reset the canvas", diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 752a864b8..13c8f6150 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -6,6 +6,7 @@ import { FlooredNumber, AppState } from "../types"; import { ExcalidrawElement, NonDeletedExcalidrawElement, + GroupId, } from "../element/types"; import { getElementAbsoluteCoords, @@ -27,6 +28,11 @@ import { getSelectedElements } from "../scene/selection"; import { renderElement, renderElementToSvg } from "./renderElement"; import colors from "../colors"; +import { + isSelectedViaGroup, + getSelectedGroupIds, + getElementsInGroup, +} from "../groups"; type HandlerRectanglesRet = keyof ReturnType; @@ -167,7 +173,10 @@ export const renderScene = ( const selections = elements.reduce((acc, element) => { const selectionColors = []; // local user - if (appState.selectedElementIds[element.id]) { + if ( + appState.selectedElementIds[element.id] && + !isSelectedViaGroup(appState, element) + ) { selectionColors.push(oc.black); } // remote users @@ -180,57 +189,96 @@ export const renderScene = ( ); } if (selectionColors.length) { - acc.push({ element, selectionColors }); + const [ + elementX1, + elementY1, + elementX2, + elementY2, + ] = getElementAbsoluteCoords(element); + acc.push({ + angle: element.angle, + elementX1, + elementY1, + elementX2, + elementY2, + selectionColors, + }); } return acc; - }, [] as { element: ExcalidrawElement; selectionColors: string[] }[]); + }, [] as { angle: number; elementX1: number; elementY1: number; elementX2: number; elementY2: number; selectionColors: string[] }[]); - selections.forEach(({ element, selectionColors }) => { - const [ + function addSelectionForGroupId(groupId: GroupId) { + const groupElements = getElementsInGroup(elements, groupId); + const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds( + groupElements, + ); + selections.push({ + angle: 0, + elementX1, + elementX2, + elementY1, + elementY2, + selectionColors: [oc.black], + }); + } + + for (const groupId of getSelectedGroupIds(appState)) { + // TODO: support multiplayer selected group IDs + addSelectionForGroupId(groupId); + } + + if (appState.editingGroupId) { + addSelectionForGroupId(appState.editingGroupId); + } + + selections.forEach( + ({ + angle, elementX1, elementY1, elementX2, elementY2, - ] = getElementAbsoluteCoords(element); + selectionColors, + }) => { + const elementWidth = elementX2 - elementX1; + const elementHeight = elementY2 - elementY1; - const elementWidth = elementX2 - elementX1; - const elementHeight = elementY2 - elementY1; + const initialLineDash = context.getLineDash(); + const lineWidth = context.lineWidth; + const lineDashOffset = context.lineDashOffset; + const strokeStyle = context.strokeStyle; - const initialLineDash = context.getLineDash(); - const lineWidth = context.lineWidth; - const lineDashOffset = context.lineDashOffset; - const strokeStyle = context.strokeStyle; + const dashedLinePadding = 4 / sceneState.zoom; + const dashWidth = 8 / sceneState.zoom; + const spaceWidth = 4 / sceneState.zoom; - const dashedLinePadding = 4 / sceneState.zoom; - const dashWidth = 8 / sceneState.zoom; - const spaceWidth = 4 / sceneState.zoom; + context.lineWidth = 1 / sceneState.zoom; - context.lineWidth = 1 / sceneState.zoom; - - const count = selectionColors.length; - for (var i = 0; i < count; ++i) { - context.strokeStyle = selectionColors[i]; - context.setLineDash([ - dashWidth, - spaceWidth + (dashWidth + spaceWidth) * (count - 1), - ]); - context.lineDashOffset = (dashWidth + spaceWidth) * i; - strokeRectWithRotation( - context, - elementX1 - dashedLinePadding, - elementY1 - dashedLinePadding, - elementWidth + dashedLinePadding * 2, - elementHeight + dashedLinePadding * 2, - elementX1 + elementWidth / 2, - elementY1 + elementHeight / 2, - element.angle, - ); - } - context.lineDashOffset = lineDashOffset; - context.strokeStyle = strokeStyle; - context.lineWidth = lineWidth; - context.setLineDash(initialLineDash); - }); + const count = selectionColors.length; + for (var i = 0; i < count; ++i) { + context.strokeStyle = selectionColors[i]; + context.setLineDash([ + dashWidth, + spaceWidth + (dashWidth + spaceWidth) * (count - 1), + ]); + context.lineDashOffset = (dashWidth + spaceWidth) * i; + strokeRectWithRotation( + context, + elementX1 - dashedLinePadding, + elementY1 - dashedLinePadding, + elementWidth + dashedLinePadding * 2, + elementHeight + dashedLinePadding * 2, + elementX1 + elementWidth / 2, + elementY1 + elementHeight / 2, + angle, + ); + } + context.lineDashOffset = lineDashOffset; + context.strokeStyle = strokeStyle; + context.lineWidth = lineWidth; + context.setLineDash(initialLineDash); + }, + ); context.translate(-sceneState.scrollX, -sceneState.scrollY); const locallySelectedElements = getSelectedElements(elements, appState); diff --git a/src/tests/__snapshots__/dragCreate.test.tsx.snap b/src/tests/__snapshots__/dragCreate.test.tsx.snap index 13ae41e28..26227494b 100644 --- a/src/tests/__snapshots__/dragCreate.test.tsx.snap +++ b/src/tests/__snapshots__/dragCreate.test.tsx.snap @@ -7,6 +7,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -43,6 +44,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -68,6 +70,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -91,6 +94,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -127,6 +131,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, diff --git a/src/tests/__snapshots__/move.test.tsx.snap b/src/tests/__snapshots__/move.test.tsx.snap index 28140a800..0239b73c7 100644 --- a/src/tests/__snapshots__/move.test.tsx.snap +++ b/src/tests/__snapshots__/move.test.tsx.snap @@ -5,6 +5,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id2", "isDeleted": false, @@ -28,6 +29,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -51,6 +53,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, diff --git a/src/tests/__snapshots__/multiPointCreate.test.tsx.snap b/src/tests/__snapshots__/multiPointCreate.test.tsx.snap index 675b8c25e..971650750 100644 --- a/src/tests/__snapshots__/multiPointCreate.test.tsx.snap +++ b/src/tests/__snapshots__/multiPointCreate.test.tsx.snap @@ -5,6 +5,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 110, "id": "id0", "isDeleted": false, @@ -46,6 +47,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 110, "id": "id0", "isDeleted": false, diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index b75d4197e..7a3515fd2 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -17,6 +17,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -37,6 +38,7 @@ Object { "id0": true, "id1": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -53,6 +55,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -76,6 +79,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -112,6 +116,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -144,6 +149,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -164,6 +170,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -207,6 +214,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -226,6 +234,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -242,6 +251,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -278,6 +288,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -321,6 +332,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -340,6 +352,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -356,6 +369,7 @@ Object { "angle": 0, "backgroundColor": "#fa5252", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -392,6 +406,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -423,6 +438,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -454,6 +470,7 @@ Object { "angle": 0, "backgroundColor": "#fa5252", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -485,6 +502,7 @@ Object { "angle": 0, "backgroundColor": "#fa5252", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -516,6 +534,7 @@ Object { "angle": 0, "backgroundColor": "#fa5252", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -559,6 +578,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -579,6 +599,7 @@ Object { "id0": true, "id1": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -595,6 +616,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -631,6 +653,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -663,6 +686,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -706,6 +730,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -727,6 +752,7 @@ Object { "id1": true, "id2": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -743,6 +769,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -779,6 +806,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -811,6 +839,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -844,6 +873,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -887,6 +917,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -907,6 +938,7 @@ Object { "id0": true, "id2": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -923,6 +955,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -946,6 +979,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -982,6 +1016,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1013,6 +1048,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1033,6 +1069,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1076,6 +1113,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -1097,6 +1135,7 @@ Object { "id1": true, "id3": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -1113,6 +1152,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1136,6 +1176,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1159,6 +1200,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -1195,6 +1237,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1226,6 +1269,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1246,6 +1290,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1277,6 +1322,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1297,6 +1343,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1317,6 +1364,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -1360,6 +1408,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -1379,6 +1428,7 @@ Object { "selectedElementIds": Object { "id7": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -1395,6 +1445,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1418,6 +1469,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1441,6 +1493,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -1464,6 +1517,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id3", "isDeleted": false, @@ -1498,6 +1552,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id4", "isDeleted": false, @@ -1532,6 +1587,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id7", "isDeleted": false, @@ -1587,6 +1643,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1618,6 +1675,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1638,6 +1696,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1669,6 +1728,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1689,6 +1749,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1709,6 +1770,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -1740,6 +1802,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1760,6 +1823,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1780,6 +1844,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -1800,6 +1865,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id3", "isDeleted": false, @@ -1842,6 +1908,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1862,6 +1929,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -1882,6 +1950,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -1902,6 +1971,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id3", "isDeleted": false, @@ -1933,6 +2003,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id4", "isDeleted": false, @@ -1975,6 +2046,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -1995,6 +2067,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -2015,6 +2088,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -2035,6 +2109,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id3", "isDeleted": false, @@ -2066,6 +2141,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id4", "isDeleted": false, @@ -2097,6 +2173,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id7", "isDeleted": false, @@ -2151,6 +2228,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2170,6 +2248,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2186,6 +2265,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2222,6 +2302,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2265,6 +2346,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2284,6 +2366,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2300,6 +2383,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2336,6 +2420,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2379,6 +2464,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2398,6 +2484,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2414,6 +2501,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2450,6 +2538,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2493,6 +2582,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2512,6 +2602,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2528,6 +2619,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2575,6 +2667,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2629,6 +2722,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2648,6 +2742,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2664,6 +2759,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2711,6 +2807,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2765,6 +2862,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2784,6 +2882,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2800,6 +2899,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2847,6 +2947,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2901,6 +3002,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -2920,6 +3022,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -2936,6 +3039,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -2983,6 +3087,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3037,6 +3142,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3056,6 +3162,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3072,6 +3179,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3108,6 +3216,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3151,6 +3260,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3170,6 +3280,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3186,6 +3297,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3222,6 +3334,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3265,6 +3378,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3284,6 +3398,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3300,6 +3415,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3347,6 +3463,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3401,6 +3518,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3420,6 +3538,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3436,6 +3555,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3472,6 +3592,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3515,6 +3636,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3534,6 +3656,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3550,6 +3673,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3597,6 +3721,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3651,6 +3776,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3671,6 +3797,7 @@ Object { "id1": true, "id4": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3687,6 +3814,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3710,6 +3838,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -3746,6 +3875,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3777,6 +3907,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3797,6 +3928,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -3840,6 +3972,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3859,6 +3992,7 @@ Object { "selectedElementIds": Object { "id0": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": true, @@ -3899,6 +4033,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -3934,6 +4069,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -3950,6 +4086,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -3986,6 +4123,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4018,6 +4156,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -4051,6 +4190,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4085,6 +4225,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -4120,6 +4261,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4156,6 +4298,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -4193,6 +4336,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4231,6 +4375,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -4270,6 +4415,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4310,6 +4456,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -4351,6 +4498,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4393,6 +4541,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -4436,6 +4585,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4480,6 +4630,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -4525,6 +4676,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4571,6 +4723,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -4618,6 +4771,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4661,6 +4815,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -4687,6 +4842,7 @@ Object { "id6": true, "id7": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -4703,6 +4859,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -4739,6 +4896,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4771,6 +4929,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -4804,6 +4963,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4838,6 +4998,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -4873,6 +5034,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4909,6 +5071,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -4946,6 +5109,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -4984,6 +5148,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -5027,6 +5192,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -5051,6 +5217,7 @@ Object { "id4": true, "id5": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -5067,6 +5234,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5103,6 +5271,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5135,6 +5304,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5168,6 +5338,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5202,6 +5373,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -5237,6 +5409,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5273,6 +5446,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5316,6 +5490,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -5338,6 +5513,7 @@ Object { "id2": true, "id3": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -5354,6 +5530,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -5390,6 +5567,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5422,6 +5600,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5455,6 +5634,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5489,6 +5669,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -5532,6 +5713,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -5552,6 +5734,7 @@ Object { "id0": true, "id1": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -5568,6 +5751,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5604,6 +5788,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5636,6 +5821,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5679,6 +5865,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -5713,6 +5900,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -5729,6 +5917,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5765,6 +5954,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5797,6 +5987,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5830,6 +6021,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5864,6 +6056,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -5899,6 +6092,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -5935,6 +6129,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -5972,6 +6167,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6010,6 +6206,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6049,6 +6246,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6089,6 +6287,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6130,6 +6329,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6172,6 +6372,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -6215,6 +6416,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6259,6 +6461,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6304,6 +6507,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6350,6 +6554,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -6393,6 +6598,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -6425,6 +6631,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -6441,6 +6648,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6477,6 +6685,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6509,6 +6718,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -6542,6 +6752,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6576,6 +6787,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6611,6 +6823,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6647,6 +6860,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -6684,6 +6898,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6722,6 +6937,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6761,6 +6977,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6801,6 +7018,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -6842,6 +7060,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6884,6 +7103,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -6927,6 +7147,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -6971,6 +7192,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7014,6 +7236,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -7044,6 +7267,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -7060,6 +7284,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -7096,6 +7321,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7128,6 +7354,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -7161,6 +7388,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7195,6 +7423,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7230,6 +7459,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7266,6 +7496,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -7303,6 +7534,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7341,6 +7573,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7380,6 +7613,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7420,6 +7654,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7461,6 +7696,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7503,6 +7739,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -7546,6 +7783,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -7574,6 +7812,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -7590,6 +7829,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7626,6 +7866,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7658,6 +7899,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -7691,6 +7933,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7725,6 +7968,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7760,6 +8004,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7796,6 +8041,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -7833,6 +8079,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7871,6 +8118,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7910,6 +8158,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -7950,6 +8199,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -7993,6 +8243,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -8020,6 +8271,7 @@ Object { "id7": true, "id8": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -8036,6 +8288,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8072,6 +8325,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8104,6 +8358,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -8137,6 +8392,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8171,6 +8427,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -8206,6 +8463,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8242,6 +8500,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -8279,6 +8538,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8317,6 +8577,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -8356,6 +8617,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8399,6 +8661,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -8424,6 +8687,7 @@ Object { "id5": true, "id6": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -8440,6 +8704,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8476,6 +8741,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8508,6 +8774,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -8541,6 +8808,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8575,6 +8843,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -8610,6 +8879,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8646,6 +8916,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -8683,6 +8954,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8726,6 +8998,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -8749,6 +9022,7 @@ Object { "id3": true, "id4": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -8765,6 +9039,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8801,6 +9076,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8833,6 +9109,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -8866,6 +9143,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8900,6 +9178,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -8935,6 +9214,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -8978,6 +9258,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -8999,6 +9280,7 @@ Object { "id1": true, "id2": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -9015,6 +9297,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9051,6 +9334,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9083,6 +9367,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -9116,6 +9401,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9159,6 +9445,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -9194,6 +9481,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -9210,6 +9498,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9246,6 +9535,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9278,6 +9568,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -9311,6 +9602,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9345,6 +9637,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -9380,6 +9673,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9416,6 +9710,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -9453,6 +9748,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9491,6 +9787,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -9530,6 +9827,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9570,6 +9868,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -9611,6 +9910,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9653,6 +9953,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -9696,6 +9997,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9740,6 +10042,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -9785,6 +10088,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9831,6 +10135,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -9878,6 +10183,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -9921,6 +10227,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -9954,6 +10261,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -9970,6 +10278,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10006,6 +10315,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10038,6 +10348,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -10071,6 +10382,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10105,6 +10417,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -10140,6 +10453,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10176,6 +10490,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -10213,6 +10528,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10251,6 +10567,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -10290,6 +10607,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10330,6 +10648,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -10371,6 +10690,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10413,6 +10733,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -10456,6 +10777,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10500,6 +10822,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -10545,6 +10868,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10588,6 +10912,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -10619,6 +10944,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -10635,6 +10961,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10671,6 +10998,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10703,6 +11031,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -10736,6 +11065,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10770,6 +11100,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -10805,6 +11136,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10841,6 +11173,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -10878,6 +11211,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10916,6 +11250,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -10955,6 +11290,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -10995,6 +11331,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -11036,6 +11373,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11078,6 +11416,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -11121,6 +11460,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11164,6 +11504,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -11193,6 +11534,7 @@ Object { "id8": true, "id9": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -11209,6 +11551,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11245,6 +11588,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11277,6 +11621,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -11310,6 +11655,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11344,6 +11690,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -11379,6 +11726,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11415,6 +11763,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 15, "id": "id0", "isDeleted": false, @@ -11452,6 +11801,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11490,6 +11840,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -11529,6 +11880,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11569,6 +11921,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 5, "id": "id0", "isDeleted": false, @@ -11610,6 +11963,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11653,6 +12007,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -11676,6 +12031,7 @@ Object { "id3": true, "id4": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -11692,6 +12048,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11715,6 +12072,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -11751,6 +12109,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11782,6 +12141,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11802,6 +12162,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -11837,6 +12198,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -11857,6 +12219,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -11900,6 +12263,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -11917,6 +12281,7 @@ Object { "scrollY": 10, "scrolledOutside": false, "selectedElementIds": Object {}, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -11957,6 +12322,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -11976,6 +12342,7 @@ Object { "selectedElementIds": Object { "id1": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": true, @@ -12016,6 +12383,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -12035,6 +12403,7 @@ Object { "selectedElementIds": Object { "id1": true, }, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, @@ -12051,6 +12420,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -12074,6 +12444,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -12097,6 +12468,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": true, @@ -12146,6 +12518,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -12166,6 +12539,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -12186,6 +12560,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 20, "id": "id2", "isDeleted": false, @@ -12235,6 +12610,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -12255,6 +12631,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -12275,6 +12652,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id2", "isDeleted": false, @@ -12322,6 +12700,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -12353,6 +12732,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id0", "isDeleted": false, @@ -12373,6 +12753,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 10, "id": "id1", "isDeleted": false, @@ -12416,6 +12797,7 @@ Object { "cursorY": 0, "draggingElement": null, "editingElement": null, + "editingGroupId": null, "elementLocked": false, "elementType": "selection", "errorMessage": null, @@ -12433,6 +12815,7 @@ Object { "scrollY": 0, "scrolledOutside": false, "selectedElementIds": Object {}, + "selectedGroupIds": Object {}, "selectionElement": null, "shouldAddWatermark": false, "shouldCacheIgnoreZoom": false, diff --git a/src/tests/__snapshots__/resize.test.tsx.snap b/src/tests/__snapshots__/resize.test.tsx.snap index a88d7bdae..390090b61 100644 --- a/src/tests/__snapshots__/resize.test.tsx.snap +++ b/src/tests/__snapshots__/resize.test.tsx.snap @@ -5,6 +5,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -28,6 +29,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, diff --git a/src/tests/__snapshots__/selection.test.tsx.snap b/src/tests/__snapshots__/selection.test.tsx.snap index 1e173f1fd..018a17ab3 100644 --- a/src/tests/__snapshots__/selection.test.tsx.snap +++ b/src/tests/__snapshots__/selection.test.tsx.snap @@ -5,6 +5,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -39,6 +40,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -73,6 +75,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -96,6 +99,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, @@ -119,6 +123,7 @@ Object { "angle": 0, "backgroundColor": "transparent", "fillStyle": "hachure", + "groupIds": Array [], "height": 50, "id": "id0", "isDeleted": false, diff --git a/src/types.ts b/src/types.ts index b5259cc1f..c4316c5df 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,7 @@ import { NonDeleted, TextAlign, ExcalidrawElement, + GroupId, } from "./element/types"; import { SHAPES } from "./shapes"; import { Point as RoughPoint } from "roughjs/bin/geometry"; @@ -67,6 +68,10 @@ export type AppState = { shouldCacheIgnoreZoom: boolean; showShortcutsDialog: boolean; zenModeEnabled: boolean; + + // groups + selectedGroupIds: { [groupId: string]: boolean }; + editingGroupId: GroupId | null; }; export type PointerCoords = Readonly<{