From 327ed0e2d1c8f79fba7a46bcd6e8ee0404a2d0de Mon Sep 17 00:00:00 2001 From: Tom Sherman Date: Thu, 7 Apr 2022 12:43:29 +0100 Subject: [PATCH] feat: Element locking (#4964) Co-authored-by: dwelle Co-authored-by: Zsolt Viczian --- src/actions/actionSelectAll.ts | 3 +- src/actions/actionToggleLock.ts | 63 +++ src/actions/index.ts | 1 + src/actions/shortcuts.ts | 2 + src/actions/types.ts | 3 +- src/charts.ts | 1 + src/components/App.tsx | 55 ++- src/components/HelpDialog.tsx | 4 + src/data/restore.ts | 1 + src/element/binding.ts | 9 +- src/element/linearElementEditor.ts | 2 +- src/element/newElement.ts | 2 + src/element/transformHandles.ts | 7 + src/element/typeChecks.ts | 11 +- src/element/types.ts | 1 + src/locales/en.json | 10 +- src/scene/comparisons.ts | 2 +- src/scene/selection.ts | 1 + .../__snapshots__/contextmenu.test.tsx.snap | 93 +++++ .../__snapshots__/dragCreate.test.tsx.snap | 5 + src/tests/__snapshots__/move.test.tsx.snap | 6 + .../multiPointCreate.test.tsx.snap | 2 + .../regressionTests.test.tsx.snap | 362 ++++++++++++++++ .../__snapshots__/selection.test.tsx.snap | 5 + src/tests/contextmenu.test.tsx | 43 +- .../data/__snapshots__/restore.test.ts.snap | 9 + src/tests/elementLocking.test.tsx | 388 ++++++++++++++++++ src/tests/fixtures/elementFixture.ts | 1 + src/tests/helpers/api.ts | 11 +- src/tests/helpers/ui.ts | 14 + .../scene/__snapshots__/export.test.ts.snap | 2 +- 31 files changed, 1066 insertions(+), 53 deletions(-) create mode 100644 src/actions/actionToggleLock.ts create mode 100644 src/tests/elementLocking.test.tsx diff --git a/src/actions/actionSelectAll.ts b/src/actions/actionSelectAll.ts index 6b793b9333..09c08d72b9 100644 --- a/src/actions/actionSelectAll.ts +++ b/src/actions/actionSelectAll.ts @@ -18,7 +18,8 @@ export const actionSelectAll = register({ selectedElementIds: elements.reduce((map, element) => { if ( !element.isDeleted && - !(isTextElement(element) && element.containerId) + !(isTextElement(element) && element.containerId) && + element.locked === false ) { map[element.id] = true; } diff --git a/src/actions/actionToggleLock.ts b/src/actions/actionToggleLock.ts new file mode 100644 index 0000000000..0f49e798d2 --- /dev/null +++ b/src/actions/actionToggleLock.ts @@ -0,0 +1,63 @@ +import { newElementWith } from "../element/mutateElement"; +import { ExcalidrawElement } from "../element/types"; +import { KEYS } from "../keys"; +import { getSelectedElements } from "../scene"; +import { arrayToMap } from "../utils"; +import { register } from "./register"; + +export const actionToggleLock = register({ + name: "toggleLock", + trackEvent: { category: "element" }, + perform: (elements, appState) => { + const selectedElements = getSelectedElements(elements, appState, true); + + if (!selectedElements.length) { + return false; + } + + const operation = getOperation(selectedElements); + const selectedElementsMap = arrayToMap(selectedElements); + + return { + elements: elements.map((element) => { + if (!selectedElementsMap.has(element.id)) { + return element; + } + + return newElementWith(element, { locked: operation === "lock" }); + }), + appState, + commitToHistory: true, + }; + }, + contextItemLabel: (elements, appState) => { + const selected = getSelectedElements(elements, appState, false); + if (selected.length === 1) { + return selected[0].locked + ? "labels.elementLock.unlock" + : "labels.elementLock.lock"; + } + + if (selected.length > 1) { + return getOperation(selected) === "lock" + ? "labels.elementLock.lockAll" + : "labels.elementLock.unlockAll"; + } + + throw new Error( + "Unexpected zero elements to lock/unlock. This should never happen.", + ); + }, + keyTest: (event, appState, elements) => { + return ( + event.key.toLocaleLowerCase() === KEYS.L && + event[KEYS.CTRL_OR_CMD] && + event.shiftKey && + getSelectedElements(elements, appState, false).length > 0 + ); + }, +}); + +const getOperation = ( + elements: readonly ExcalidrawElement[], +): "lock" | "unlock" => (elements.some((el) => !el.locked) ? "lock" : "unlock"); diff --git a/src/actions/index.ts b/src/actions/index.ts index ffb20bc3f3..d8ec676352 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -84,3 +84,4 @@ export { actionToggleZenMode } from "./actionToggleZenMode"; export { actionToggleStats } from "./actionToggleStats"; export { actionUnbindText, actionBindText } from "./actionBoundText"; export { actionLink } from "../element/Hyperlink"; +export { actionToggleLock } from "./actionToggleLock"; diff --git a/src/actions/shortcuts.ts b/src/actions/shortcuts.ts index 2227dc1d94..2d37f20c21 100644 --- a/src/actions/shortcuts.ts +++ b/src/actions/shortcuts.ts @@ -29,6 +29,7 @@ export type ShortcutName = SubtypeOf< | "flipHorizontal" | "flipVertical" | "hyperlink" + | "toggleLock" >; const shortcutMap: Record = { @@ -67,6 +68,7 @@ const shortcutMap: Record = { flipVertical: [getShortcutKey("Shift+V")], viewMode: [getShortcutKey("Alt+R")], hyperlink: [getShortcutKey("CtrlOrCmd+K")], + toggleLock: [getShortcutKey("CtrlOrCmd+Shift+L")], }; export const getShortcutFromShortcutName = (name: ShortcutName) => { diff --git a/src/actions/types.ts b/src/actions/types.ts index 810f69c157..f9cd9186fb 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -111,7 +111,8 @@ export type ActionName = | "unbindText" | "hyperlink" | "eraser" - | "bindText"; + | "bindText" + | "toggleLock"; export type PanelComponentProps = { elements: readonly ExcalidrawElement[]; diff --git a/src/charts.ts b/src/charts.ts index 1479bce683..4e057b9dc3 100644 --- a/src/charts.ts +++ b/src/charts.ts @@ -167,6 +167,7 @@ const commonProps = { strokeStyle: "solid", strokeWidth: 1, verticalAlign: VERTICAL_ALIGN.MIDDLE, + locked: false, } as const; const getChartDimentions = (spreadsheet: Spreadsheet) => { diff --git a/src/components/App.tsx b/src/components/App.tsx index 6aaca20187..ea3969d8dc 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -31,6 +31,7 @@ import { actionBindText, actionUngroup, actionLink, + actionToggleLock, } from "../actions"; import { createRedoAction, createUndoAction } from "../actions/actionHistory"; import { ActionManager } from "../actions/manager"; @@ -1134,7 +1135,7 @@ class App extends React.Component { prevState.activeTool !== this.state.activeTool && multiElement != null && isBindingEnabled(this.state) && - isBindingElement(multiElement) + isBindingElement(multiElement, false) ) { maybeBindLinearElement( multiElement, @@ -1546,6 +1547,7 @@ class App extends React.Component { fontFamily: this.state.currentItemFontFamily, textAlign: this.state.currentItemTextAlign, verticalAlign: DEFAULT_VERTICAL_ALIGN, + locked: false, }); this.scene.replaceAllElements([ @@ -2126,12 +2128,14 @@ class App extends React.Component { of all hit elements */ preferSelected?: boolean; includeBoundTextElement?: boolean; + includeLockedElements?: boolean; }, ): NonDeleted | null { const allHitElements = this.getElementsAtPosition( x, y, opts?.includeBoundTextElement, + opts?.includeLockedElements, ); if (allHitElements.length > 1) { if (opts?.preferSelected) { @@ -2164,14 +2168,19 @@ class App extends React.Component { x: number, y: number, includeBoundTextElement: boolean = false, + includeLockedElements: boolean = false, ): NonDeleted[] { - const elements = includeBoundTextElement - ? this.scene.getElements() - : this.scene - .getElements() - .filter( - (element) => !(isTextElement(element) && element.containerId), - ); + const elements = + includeBoundTextElement && includeLockedElements + ? this.scene.getElements() + : this.scene + .getElements() + .filter( + (element) => + (includeLockedElements || !element.locked) && + (includeBoundTextElement || + !(isTextElement(element) && element.containerId)), + ); return getElementsAtPosition(elements, (element) => hitTest(element, this.state, x, y), @@ -2213,7 +2222,7 @@ class App extends React.Component { if (selectedElements.length === 1) { if (isTextElement(selectedElements[0])) { existingTextElement = selectedElements[0]; - } else if (isTextBindableContainer(selectedElements[0])) { + } else if (isTextBindableContainer(selectedElements[0], false)) { container = selectedElements[0]; existingTextElement = getBoundTextElement(container); } @@ -2233,7 +2242,8 @@ class App extends React.Component { this.scene .getElements() .filter( - (ele) => isTextBindableContainer(ele) && !getBoundTextElement(ele), + (ele) => + isTextBindableContainer(ele, false) && !getBoundTextElement(ele), ), sceneX, sceneY, @@ -2291,6 +2301,7 @@ class App extends React.Component { : DEFAULT_VERTICAL_ALIGN, containerId: container?.id ?? undefined, groupIds: container?.groupIds ?? [], + locked: false, }); this.setState({ editingElement: element }); @@ -2597,7 +2608,7 @@ class App extends React.Component { // Hovering with a selected tool or creating new linear element via click // and point const { draggingElement } = this.state; - if (isBindingElement(draggingElement)) { + if (isBindingElement(draggingElement, false)) { this.maybeSuggestBindingsForLinearElementAtCoords( draggingElement, [scenePointer], @@ -2780,7 +2791,8 @@ class App extends React.Component { this.isHittingCommonBoundingBoxOfSelectedElements( scenePointer, selectedElements, - )) + )) && + !hitElement?.locked ) { setCursor(this.canvas, CURSOR_TYPE.MOVE); } else { @@ -2796,6 +2808,10 @@ class App extends React.Component { ) => { const updateElementIds = (elements: ExcalidrawElement[]) => { elements.forEach((element) => { + if (element.locked) { + return; + } + idsToUpdate.push(element.id); if (event.altKey) { if ( @@ -3617,6 +3633,7 @@ class App extends React.Component { opacity: this.state.currentItemOpacity, strokeSharpness: this.state.currentItemLinearStrokeSharpness, simulatePressure: event.pressure === 0.5, + locked: false, }); this.setState((prevState) => ({ @@ -3672,6 +3689,7 @@ class App extends React.Component { roughness: this.state.currentItemRoughness, opacity: this.state.currentItemOpacity, strokeSharpness: this.state.currentItemLinearStrokeSharpness, + locked: false, }); return element; @@ -3759,6 +3777,7 @@ class App extends React.Component { strokeSharpness: this.state.currentItemLinearStrokeSharpness, startArrowhead, endArrowhead, + locked: false, }); this.setState((prevState) => ({ selectedElementIds: { @@ -3807,6 +3826,7 @@ class App extends React.Component { roughness: this.state.currentItemRoughness, opacity: this.state.currentItemOpacity, strokeSharpness: this.state.currentItemStrokeSharpness, + locked: false, }); if (element.type === "selection") { @@ -4106,7 +4126,7 @@ class App extends React.Component { }); } - if (isBindingElement(draggingElement)) { + if (isBindingElement(draggingElement, false)) { // When creating a linear element by dragging this.maybeSuggestBindingsForLinearElementAtCoords( draggingElement, @@ -4385,7 +4405,7 @@ class App extends React.Component { } else if (pointerDownState.drag.hasOccurred && !multiElement) { if ( isBindingEnabled(this.state) && - isBindingElement(draggingElement) + isBindingElement(draggingElement, false) ) { maybeBindLinearElement( draggingElement, @@ -5303,7 +5323,10 @@ class App extends React.Component { } const { x, y } = viewportCoordsToSceneCoords(event, this.state); - const element = this.getElementAtPosition(x, y, { preferSelected: true }); + const element = this.getElementAtPosition(x, y, { + preferSelected: true, + includeLockedElements: true, + }); const type = element ? "element" : "canvas"; @@ -5615,6 +5638,8 @@ class App extends React.Component { (maybeFlipHorizontal || maybeFlipVertical) && separator, actionLink.contextItemPredicate(elements, this.state) && actionLink, actionDuplicateSelection, + actionToggleLock, + separator, actionDeleteSelected, ], top, diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index 66e90d55a9..34d24f4839 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -363,6 +363,10 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { getShortcutKey(`Alt+${t("helpDialog.drag")}`), ]} /> + - isBindableElement(element) && bindingBorderTest(element, pointerCoords), + isBindableElement(element, false) && + bindingBorderTest(element, pointerCoords), ); return hoveredElement as NonDeleted | null; }; @@ -456,13 +457,13 @@ export const getEligibleElementsForBinding = ( ): SuggestedBinding[] => { const includedElementIds = new Set(elements.map(({ id }) => id)); return elements.flatMap((element) => - isBindingElement(element) + isBindingElement(element, false) ? (getElligibleElementsForBindingElement( element as NonDeleted, ).filter( (element) => !includedElementIds.has(element.id), ) as SuggestedBinding[]) - : isBindableElement(element) + : isBindableElement(element, false) ? getElligibleElementsForBindableElementAndWhere(element).filter( (binding) => !includedElementIds.has(binding[0].id), ) @@ -508,7 +509,7 @@ const getElligibleElementsForBindableElementAndWhere = ( return Scene.getScene(bindableElement)! .getElements() .map((element) => { - if (!isBindingElement(element)) { + if (!isBindingElement(element, false)) { return null; } const canBindStart = isLinearElementEligibleForNewBindingByBindable( diff --git a/src/element/linearElementEditor.ts b/src/element/linearElementEditor.ts index 51ddc4926b..b2566381bd 100644 --- a/src/element/linearElementEditor.ts +++ b/src/element/linearElementEditor.ts @@ -205,7 +205,7 @@ export class LinearElementEditor { ); // suggest bindings for first and last point if selected - if (isBindingElement(element)) { + if (isBindingElement(element, false)) { const coords: { x: number; y: number }[] = []; const firstSelectedIndex = selectedPointsIndices[0]; diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 11a0f23dd8..234e06c399 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -56,6 +56,7 @@ const _newElementBase = ( strokeSharpness, boundElements = null, link = null, + locked, ...rest }: ElementConstructorOpts & Omit, "type">, ) => { @@ -83,6 +84,7 @@ const _newElementBase = ( boundElements, updated: getUpdatedTimestamp(), link, + locked, }; return element; }; diff --git a/src/element/transformHandles.ts b/src/element/transformHandles.ts index 4350891b15..f93f74d8a8 100644 --- a/src/element/transformHandles.ts +++ b/src/element/transformHandles.ts @@ -222,6 +222,13 @@ export const getTransformHandles = ( zoom: Zoom, pointerType: PointerType = "mouse", ): TransformHandles => { + // so that when locked element is selected (especially when you toggle lock + // via keyboard) the locked element is visually distinct, indicating + // you can't move/resize + if (element.locked) { + return {}; + } + let omitSides: { [T in TransformHandleType]?: boolean } = {}; if ( element.type === "arrow" || diff --git a/src/element/typeChecks.ts b/src/element/typeChecks.ts index 58d9492fd5..fdebca875c 100644 --- a/src/element/typeChecks.ts +++ b/src/element/typeChecks.ts @@ -70,8 +70,13 @@ export const isLinearElementType = ( export const isBindingElement = ( element?: ExcalidrawElement | null, + includeLocked = true, ): element is ExcalidrawLinearElement => { - return element != null && isBindingElementType(element.type); + return ( + element != null && + (!element.locked || includeLocked === true) && + isBindingElementType(element.type) + ); }; export const isBindingElementType = ( @@ -82,9 +87,11 @@ export const isBindingElementType = ( export const isBindableElement = ( element: ExcalidrawElement | null, + includeLocked = true, ): element is ExcalidrawBindableElement => { return ( element != null && + (!element.locked || includeLocked === true) && (element.type === "rectangle" || element.type === "diamond" || element.type === "ellipse" || @@ -95,9 +102,11 @@ export const isBindableElement = ( export const isTextBindableContainer = ( element: ExcalidrawElement | null, + includeLocked = true, ): element is ExcalidrawTextContainer => { return ( element != null && + (!element.locked || includeLocked === true) && (element.type === "rectangle" || element.type === "diamond" || element.type === "ellipse" || diff --git a/src/element/types.ts b/src/element/types.ts index f7177e44c7..0834efd6c7 100644 --- a/src/element/types.ts +++ b/src/element/types.ts @@ -55,6 +55,7 @@ type _ExcalidrawElementBase = Readonly<{ /** epoch (ms) timestamp of last element update */ updated: number; link: string | null; + locked: boolean; }>; export type ExcalidrawSelectionElement = _ExcalidrawElementBase & { diff --git a/src/locales/en.json b/src/locales/en.json index def36a102c..7e69365c1e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -113,6 +113,12 @@ "edit": "Edit link", "create": "Create link", "label": "Link" + }, + "elementLock": { + "lock": "Lock", + "unlock": "Unlock", + "lockAll": "Lock all", + "unlockAll": "Unlock all" } }, "buttons": { @@ -292,7 +298,8 @@ "title": "Help", "view": "View", "zoomToFit": "Zoom to fit all elements", - "zoomToSelection": "Zoom to selection" + "zoomToSelection": "Zoom to selection", + "toggleElementLock": "Lock/unlock selection" }, "clearCanvasDialog": { "title": "Clear canvas" @@ -336,7 +343,6 @@ "noteItems": "Each library item must have its own name so it's filterable. The following library items will be included:", "atleastOneLibItem": "Please select at least one library item to get started" }, - "publishSuccessDialog": { "title": "Library submitted", "content": "Thank you {{authorName}}. Your library has been submitted for review. You can track the status", diff --git a/src/scene/comparisons.ts b/src/scene/comparisons.ts index 57b3672d6e..d94be0a69b 100644 --- a/src/scene/comparisons.ts +++ b/src/scene/comparisons.ts @@ -91,5 +91,5 @@ export const getTextBindableContainerAtPosition = ( break; } } - return isTextBindableContainer(hitElement) ? hitElement : null; + return isTextBindableContainer(hitElement, false) ? hitElement : null; }; diff --git a/src/scene/selection.ts b/src/scene/selection.ts index a5abf7e430..39d3518b13 100644 --- a/src/scene/selection.ts +++ b/src/scene/selection.ts @@ -17,6 +17,7 @@ export const getElementsWithinSelection = ( getElementBounds(element); return ( + element.locked === false && element.type !== "selection" && !isBoundToContainer(element) && selectionX1 <= elementX1 && diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap index 7d5029a9f7..9451f7dc40 100644 --- a/src/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap @@ -94,6 +94,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -149,6 +150,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -268,6 +270,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -296,6 +299,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -351,6 +355,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -390,6 +395,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -415,6 +421,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -454,6 +461,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -479,6 +487,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -598,6 +607,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -626,6 +636,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -681,6 +692,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -720,6 +732,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -745,6 +758,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -784,6 +798,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -809,6 +824,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -928,6 +944,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -983,6 +1000,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1100,6 +1118,7 @@ Object { "id": "id0", "isDeleted": true, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1155,6 +1174,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1192,6 +1212,7 @@ Object { "id": "id0", "isDeleted": true, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1311,6 +1332,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1339,6 +1361,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -1394,6 +1417,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1433,6 +1457,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1458,6 +1483,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -1585,6 +1611,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1615,6 +1642,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -1670,6 +1698,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1709,6 +1738,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1734,6 +1764,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -1779,6 +1810,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -1806,6 +1838,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -1925,6 +1958,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 60, "roughness": 2, "seed": 1278240551, @@ -1953,6 +1987,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 60, "roughness": 2, "seed": 400692809, @@ -2008,6 +2043,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2047,6 +2083,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2072,6 +2109,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2111,6 +2149,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2136,6 +2175,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2175,6 +2215,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2200,6 +2241,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2239,6 +2281,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2264,6 +2307,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2303,6 +2347,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2328,6 +2373,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2367,6 +2413,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2392,6 +2439,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2431,6 +2479,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2456,6 +2505,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 2, "seed": 400692809, @@ -2495,6 +2545,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2520,6 +2571,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 60, "roughness": 2, "seed": 400692809, @@ -2559,6 +2611,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 60, "roughness": 2, "seed": 1278240551, @@ -2584,6 +2637,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 60, "roughness": 2, "seed": 400692809, @@ -2703,6 +2757,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2731,6 +2786,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2786,6 +2842,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2825,6 +2882,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -2850,6 +2908,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2889,6 +2948,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -2914,6 +2974,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3033,6 +3094,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3061,6 +3123,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3116,6 +3179,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3155,6 +3219,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3180,6 +3245,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3219,6 +3285,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3244,6 +3311,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3367,6 +3435,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -3395,6 +3464,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -3450,6 +3520,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -3489,6 +3560,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -3514,6 +3586,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -3559,6 +3632,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -3586,6 +3660,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -3627,6 +3702,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -3652,6 +3728,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -3777,6 +3854,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3805,6 +3883,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3860,6 +3939,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3899,6 +3979,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3924,6 +4005,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -4053,6 +4135,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4083,6 +4166,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -4138,6 +4222,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4177,6 +4262,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4202,6 +4288,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -4248,6 +4335,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4275,6 +4363,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -4582,6 +4671,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -4610,6 +4700,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4638,6 +4729,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -4693,6 +4785,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, diff --git a/src/tests/__snapshots__/dragCreate.test.tsx.snap b/src/tests/__snapshots__/dragCreate.test.tsx.snap index 92848d4d53..ef7b8a685b 100644 --- a/src/tests/__snapshots__/dragCreate.test.tsx.snap +++ b/src/tests/__snapshots__/dragCreate.test.tsx.snap @@ -16,6 +16,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -58,6 +59,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -88,6 +90,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -119,6 +122,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -161,6 +165,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, diff --git a/src/tests/__snapshots__/move.test.tsx.snap b/src/tests/__snapshots__/move.test.tsx.snap index 7971a53c45..68a7338374 100644 --- a/src/tests/__snapshots__/move.test.tsx.snap +++ b/src/tests/__snapshots__/move.test.tsx.snap @@ -11,6 +11,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -39,6 +40,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -67,6 +69,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -100,6 +103,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -133,6 +137,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -168,6 +173,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ diff --git a/src/tests/__snapshots__/multiPointCreate.test.tsx.snap b/src/tests/__snapshots__/multiPointCreate.test.tsx.snap index 8fa46f1fef..2289ec869e 100644 --- a/src/tests/__snapshots__/multiPointCreate.test.tsx.snap +++ b/src/tests/__snapshots__/multiPointCreate.test.tsx.snap @@ -17,6 +17,7 @@ Object { 110, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -67,6 +68,7 @@ Object { 110, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 9b22aab362..d8566f5ce5 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -105,6 +105,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -135,6 +136,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -165,6 +167,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -220,6 +223,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -259,6 +263,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -284,6 +289,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -323,6 +329,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -348,6 +355,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -373,6 +381,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -417,6 +426,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -444,6 +454,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -471,6 +482,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -608,6 +620,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -638,6 +651,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -668,6 +682,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -723,6 +738,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -762,6 +778,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -787,6 +804,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -826,6 +844,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -851,6 +870,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -876,6 +896,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -919,6 +940,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -946,6 +968,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -973,6 +996,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -1096,6 +1120,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1127,6 +1152,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1157,6 +1183,7 @@ Object { "id": "id7", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -1212,6 +1239,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1251,6 +1279,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1276,6 +1305,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1322,6 +1352,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1349,6 +1380,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1391,6 +1423,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1418,6 +1451,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1459,6 +1493,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1486,6 +1521,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1511,6 +1547,7 @@ Object { "id": "id7", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -1559,6 +1596,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1587,6 +1625,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1614,6 +1653,7 @@ Object { "id": "id7", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -1657,6 +1697,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1685,6 +1726,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1712,6 +1754,7 @@ Object { "id": "id7", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -1755,6 +1798,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1783,6 +1827,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -1810,6 +1855,7 @@ Object { "id": "id7", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -1932,6 +1978,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -1987,6 +2034,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2027,6 +2075,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2154,6 +2203,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -2184,6 +2234,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2214,6 +2265,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -2269,6 +2321,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2308,6 +2361,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2333,6 +2387,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -2372,6 +2427,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2397,6 +2453,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -2422,6 +2479,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -2466,6 +2524,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -2493,6 +2552,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2520,6 +2580,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -2642,6 +2703,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -2670,6 +2732,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2725,6 +2788,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2765,6 +2829,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -2790,6 +2855,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2909,6 +2975,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -2964,6 +3031,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3086,6 +3154,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3114,6 +3183,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3142,6 +3212,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 2019559783, @@ -3197,6 +3268,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3236,6 +3308,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3261,6 +3334,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3300,6 +3374,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3325,6 +3400,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3350,6 +3426,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 2019559783, @@ -3390,6 +3467,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -3415,6 +3493,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 453191, @@ -3440,6 +3519,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 2019559783, @@ -3559,6 +3639,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3614,6 +3695,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3653,6 +3735,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3692,6 +3775,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3814,6 +3898,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3869,6 +3954,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -3909,6 +3995,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4033,6 +4120,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4088,6 +4176,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4128,6 +4217,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4169,6 +4259,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4291,6 +4382,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4319,6 +4411,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4374,6 +4467,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4413,6 +4507,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4438,6 +4533,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4561,6 +4657,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4589,6 +4686,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4617,6 +4715,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -4672,6 +4771,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4711,6 +4811,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4736,6 +4837,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4775,6 +4877,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -4800,6 +4903,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -4825,6 +4929,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -4884,6 +4989,7 @@ Object { "id": "id3", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 2019559783, @@ -4950,6 +5056,7 @@ Object { "id": "id3", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 2019559783, @@ -4994,6 +5101,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5022,6 +5130,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5077,6 +5186,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5116,6 +5226,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5141,6 +5252,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5200,6 +5312,7 @@ Object { "id": "id3", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 2019559783, @@ -5286,6 +5399,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5314,6 +5428,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5369,6 +5484,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5408,6 +5524,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5433,6 +5550,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5492,6 +5610,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5556,6 +5675,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5600,6 +5720,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5655,6 +5776,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5714,6 +5836,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -5798,6 +5921,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5853,6 +5977,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -5974,6 +6099,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6004,6 +6130,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6034,6 +6161,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -6089,6 +6217,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6128,6 +6257,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6153,6 +6283,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6192,6 +6323,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6217,6 +6349,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6242,6 +6375,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -6287,6 +6421,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6314,6 +6449,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6341,6 +6477,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -6467,6 +6604,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6495,6 +6633,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6550,6 +6689,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6589,6 +6729,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6614,6 +6755,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6656,6 +6798,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6681,6 +6824,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6800,6 +6944,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -6828,6 +6973,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -6856,6 +7002,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -6887,6 +7034,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -6930,6 +7078,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -6976,6 +7125,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7026,6 +7176,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7074,6 +7225,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7149,6 +7301,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7188,6 +7341,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7213,6 +7367,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -7252,6 +7407,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7277,6 +7433,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -7302,6 +7459,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -7341,6 +7499,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7366,6 +7525,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -7391,6 +7551,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -7419,6 +7580,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7470,6 +7632,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7495,6 +7658,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -7520,6 +7684,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -7548,6 +7713,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7588,6 +7754,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7639,6 +7806,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7664,6 +7832,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -7689,6 +7858,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -7717,6 +7887,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7757,6 +7928,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7800,6 +7972,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7851,6 +8024,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -7876,6 +8050,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -7901,6 +8076,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -7929,6 +8105,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -7969,6 +8146,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8012,6 +8190,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8067,6 +8246,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -8092,6 +8272,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -8117,6 +8298,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -8145,6 +8327,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8185,6 +8368,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8228,6 +8412,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8275,6 +8460,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8326,6 +8512,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -8351,6 +8538,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -8376,6 +8564,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -8404,6 +8593,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8444,6 +8634,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8487,6 +8678,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8534,6 +8726,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8589,6 +8782,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -8614,6 +8808,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -8639,6 +8834,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -8667,6 +8863,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8707,6 +8904,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8750,6 +8948,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8797,6 +8996,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8842,6 +9042,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -8986,6 +9187,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9014,6 +9216,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -9042,6 +9245,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -9097,6 +9301,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9136,6 +9341,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9161,6 +9367,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -9200,6 +9407,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9225,6 +9433,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -9250,6 +9459,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -9375,6 +9585,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9403,6 +9614,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -9456,6 +9668,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9495,6 +9708,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9520,6 +9734,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -9643,6 +9858,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9671,6 +9887,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -9727,6 +9944,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9752,6 +9970,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -9876,6 +10095,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9904,6 +10124,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -9960,6 +10181,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -9985,6 +10207,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -10026,6 +10249,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10051,6 +10275,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1278240551, @@ -10170,6 +10395,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10225,6 +10451,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10344,6 +10571,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10399,6 +10627,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10518,6 +10747,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10573,6 +10803,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -10695,6 +10926,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -10765,6 +10997,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -10899,6 +11132,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -10969,6 +11203,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11104,6 +11339,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11183,6 +11419,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11325,6 +11562,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11395,6 +11633,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11526,6 +11765,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -11581,6 +11821,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -11703,6 +11944,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11773,6 +12015,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -11904,6 +12147,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -11959,6 +12203,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12078,6 +12323,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12133,6 +12379,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12256,6 +12503,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -12335,6 +12583,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -12487,6 +12736,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 915032327, @@ -12517,6 +12767,7 @@ Object { "id": "id1_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 747212839, @@ -12547,6 +12798,7 @@ Object { "id": "id2_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 760410951, @@ -12577,6 +12829,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12607,6 +12860,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -12637,6 +12891,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -12692,6 +12947,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12731,6 +12987,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12756,6 +13013,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -12795,6 +13053,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12820,6 +13079,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -12845,6 +13105,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -12891,6 +13152,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -12918,6 +13180,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -12945,6 +13208,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -12992,6 +13256,7 @@ Object { "id": "id0_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 915032327, @@ -13019,6 +13284,7 @@ Object { "id": "id1_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 747212839, @@ -13046,6 +13312,7 @@ Object { "id": "id2_copy", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 760410951, @@ -13073,6 +13340,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13100,6 +13368,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -13127,6 +13396,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -13250,6 +13520,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13278,6 +13549,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -13333,6 +13605,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13372,6 +13645,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13397,6 +13671,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -13731,6 +14006,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13786,6 +14062,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13914,6 +14191,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -13942,6 +14220,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -13997,6 +14276,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14036,6 +14316,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14061,6 +14342,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14104,6 +14386,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14129,6 +14412,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14248,6 +14532,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14303,6 +14588,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14342,6 +14628,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14475,6 +14762,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14506,6 +14794,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14537,6 +14826,7 @@ Object { "id": "id5", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1014066025, @@ -14568,6 +14858,7 @@ Object { "id": "id6", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -14623,6 +14914,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14662,6 +14954,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14687,6 +14980,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14733,6 +15027,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14760,6 +15055,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14801,6 +15097,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14828,6 +15125,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14853,6 +15151,7 @@ Object { "id": "id5", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1014066025, @@ -14894,6 +15193,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -14921,6 +15221,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -14946,6 +15247,7 @@ Object { "id": "id5", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1014066025, @@ -14971,6 +15273,7 @@ Object { "id": "id6", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -15017,6 +15320,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15044,6 +15348,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15071,6 +15376,7 @@ Object { "id": "id5", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1014066025, @@ -15098,6 +15404,7 @@ Object { "id": "id6", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -15145,6 +15452,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15173,6 +15481,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15201,6 +15510,7 @@ Object { "id": "id5", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1014066025, @@ -15229,6 +15539,7 @@ Object { "id": "id6", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 400692809, @@ -15457,6 +15768,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15488,6 +15800,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15519,6 +15832,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -15574,6 +15888,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15613,6 +15928,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15638,6 +15954,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15677,6 +15994,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15702,6 +16020,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15727,6 +16046,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -15772,6 +16092,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15799,6 +16120,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15826,6 +16148,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -15869,6 +16192,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -15896,6 +16220,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15923,6 +16248,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -15968,6 +16294,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -15996,6 +16323,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16024,6 +16352,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -16070,6 +16399,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -16098,6 +16428,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16126,6 +16457,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -16185,6 +16517,7 @@ Object { "id": "id4", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1116226695, @@ -16253,6 +16586,7 @@ Object { "id": "id4", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 1116226695, @@ -16297,6 +16631,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16325,6 +16660,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -16353,6 +16689,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -16408,6 +16745,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16447,6 +16785,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16472,6 +16811,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -16511,6 +16851,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16536,6 +16877,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -16561,6 +16903,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -16620,6 +16963,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -16686,6 +17030,7 @@ Object { "id": "id2", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 401146281, @@ -16730,6 +17075,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16758,6 +17104,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -16813,6 +17160,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16852,6 +17200,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -16877,6 +17226,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -17103,6 +17453,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -17131,6 +17482,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -17165,6 +17517,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -17220,6 +17573,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -17245,6 +17599,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -17276,6 +17631,7 @@ Object { 20, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -17331,6 +17687,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -17356,6 +17713,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, @@ -17387,6 +17745,7 @@ Object { 10, ], "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -17451,6 +17810,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -17490,6 +17850,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -17515,6 +17876,7 @@ Object { "id": "id1", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 449462985, diff --git a/src/tests/__snapshots__/selection.test.tsx.snap b/src/tests/__snapshots__/selection.test.tsx.snap index e89f709da9..d3f6ae5277 100644 --- a/src/tests/__snapshots__/selection.test.tsx.snap +++ b/src/tests/__snapshots__/selection.test.tsx.snap @@ -14,6 +14,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -57,6 +58,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -97,6 +99,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -125,6 +128,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, @@ -153,6 +157,7 @@ Object { "id": "id0", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "roughness": 1, "seed": 337897, diff --git a/src/tests/contextmenu.test.tsx b/src/tests/contextmenu.test.tsx index 4dfcbb3c7d..8f5f3263f4 100644 --- a/src/tests/contextmenu.test.tsx +++ b/src/tests/contextmenu.test.tsx @@ -36,10 +36,6 @@ const checkpoint = (name: string) => { const mouse = new Pointer("mouse"); -const queryContextMenu = () => { - return GlobalTestState.renderResult.container.querySelector(".context-menu"); -}; - // Unmount ReactDOM from root ReactDOM.unmountComponentAtNode(document.getElementById("root")!); @@ -83,7 +79,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const contextMenuOptions = contextMenu?.querySelectorAll(".context-menu li"); const expectedShortcutNames: ShortcutName[] = [ @@ -113,7 +109,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const contextMenuOptions = contextMenu?.querySelectorAll(".context-menu li"); const expectedShortcutNames: ShortcutName[] = [ @@ -129,6 +125,7 @@ describe("contextMenu element", () => { "bringToFront", "duplicateSelection", "hyperlink", + "toggleLock", ]; expect(contextMenu).not.toBeNull(); @@ -166,7 +163,7 @@ describe("contextMenu element", () => { clientX: 100, clientY: 100, }); - expect(queryContextMenu()).not.toBeNull(); + expect(UI.queryContextMenu()).not.toBeNull(); expect(API.getSelectedElement().id).toBe(rect1.id); // higher z-index @@ -176,7 +173,7 @@ describe("contextMenu element", () => { clientX: 100, clientY: 100, }); - expect(queryContextMenu()).not.toBeNull(); + expect(UI.queryContextMenu()).not.toBeNull(); expect(API.getSelectedElement().id).toBe(rect2.id); }); @@ -201,7 +198,7 @@ describe("contextMenu element", () => { clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const contextMenuOptions = contextMenu?.querySelectorAll(".context-menu li"); const expectedShortcutNames: ShortcutName[] = [ @@ -215,6 +212,7 @@ describe("contextMenu element", () => { "sendToBack", "bringToFront", "duplicateSelection", + "toggleLock", ]; expect(contextMenu).not.toBeNull(); @@ -251,7 +249,7 @@ describe("contextMenu element", () => { clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const contextMenuOptions = contextMenu?.querySelectorAll(".context-menu li"); const expectedShortcutNames: ShortcutName[] = [ @@ -265,6 +263,7 @@ describe("contextMenu element", () => { "sendToBack", "bringToFront", "duplicateSelection", + "toggleLock", ]; expect(contextMenu).not.toBeNull(); @@ -286,7 +285,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); expect(copiedStyles).toBe("{}"); fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!); expect(copiedStyles).not.toBe("{}"); @@ -328,7 +327,7 @@ describe("contextMenu element", () => { clientX: 40, clientY: 40, }); - let contextMenu = queryContextMenu(); + let contextMenu = UI.queryContextMenu(); fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!); const secondRect = JSON.parse(copiedStyles); expect(secondRect.id).toBe(h.elements[1].id); @@ -340,7 +339,7 @@ describe("contextMenu element", () => { clientX: 10, clientY: 10, }); - contextMenu = queryContextMenu(); + contextMenu = UI.queryContextMenu(); fireEvent.click(queryByText(contextMenu as HTMLElement, "Paste styles")!); const firstRect = API.getSelectedElement(); @@ -364,7 +363,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); fireEvent.click(queryAllByText(contextMenu as HTMLElement, "Delete")[0]); expect(API.getSelectedElements()).toHaveLength(0); expect(h.elements[0].isDeleted).toBe(true); @@ -380,7 +379,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); fireEvent.click(queryByText(contextMenu as HTMLElement, "Add to library")!); await waitFor(() => { @@ -401,7 +400,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); fireEvent.click(queryByText(contextMenu as HTMLElement, "Duplicate")!); expect(h.elements).toHaveLength(2); const { id: _id0, seed: _seed0, x: _x0, y: _y0, ...rect1 } = h.elements[0]; @@ -424,7 +423,7 @@ describe("contextMenu element", () => { clientX: 40, clientY: 40, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const elementsBefore = h.elements; fireEvent.click(queryByText(contextMenu as HTMLElement, "Send backward")!); expect(elementsBefore[0].id).toEqual(h.elements[1].id); @@ -446,7 +445,7 @@ describe("contextMenu element", () => { clientX: 10, clientY: 10, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const elementsBefore = h.elements; fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring forward")!); expect(elementsBefore[0].id).toEqual(h.elements[1].id); @@ -468,7 +467,7 @@ describe("contextMenu element", () => { clientX: 40, clientY: 40, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const elementsBefore = h.elements; fireEvent.click(queryByText(contextMenu as HTMLElement, "Send to back")!); expect(elementsBefore[1].id).toEqual(h.elements[0].id); @@ -489,7 +488,7 @@ describe("contextMenu element", () => { clientX: 10, clientY: 10, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); const elementsBefore = h.elements; fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring to front")!); expect(elementsBefore[0].id).toEqual(h.elements[1].id); @@ -514,7 +513,7 @@ describe("contextMenu element", () => { clientX: 1, clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); fireEvent.click( queryByText(contextMenu as HTMLElement, "Group selection")!, ); @@ -547,7 +546,7 @@ describe("contextMenu element", () => { clientY: 1, }); - const contextMenu = queryContextMenu(); + const contextMenu = UI.queryContextMenu(); expect(contextMenu).not.toBeNull(); fireEvent.click( queryByText(contextMenu as HTMLElement, "Ungroup selection")!, diff --git a/src/tests/data/__snapshots__/restore.test.ts.snap b/src/tests/data/__snapshots__/restore.test.ts.snap index 870b12165e..b064931543 100644 --- a/src/tests/data/__snapshots__/restore.test.ts.snap +++ b/src/tests/data/__snapshots__/restore.test.ts.snap @@ -14,6 +14,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -58,6 +59,7 @@ Object { "id": "1", "isDeleted": false, "link": null, + "locked": false, "opacity": 10, "roughness": 2, "seed": Any, @@ -90,6 +92,7 @@ Object { "id": "2", "isDeleted": false, "link": null, + "locked": false, "opacity": 10, "roughness": 2, "seed": Any, @@ -122,6 +125,7 @@ Object { "id": "3", "isDeleted": false, "link": null, + "locked": false, "opacity": 10, "roughness": 2, "seed": Any, @@ -151,6 +155,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [], "pressures": Array [], @@ -185,6 +190,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -228,6 +234,7 @@ Object { "isDeleted": false, "lastCommittedPoint": null, "link": null, + "locked": false, "opacity": 100, "points": Array [ Array [ @@ -272,6 +279,7 @@ Object { "id": "id-text01", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "originalText": "text", "roughness": 1, @@ -308,6 +316,7 @@ Object { "id": "id-text01", "isDeleted": false, "link": null, + "locked": false, "opacity": 100, "originalText": "test", "roughness": 1, diff --git a/src/tests/elementLocking.test.tsx b/src/tests/elementLocking.test.tsx new file mode 100644 index 0000000000..f23e2d214c --- /dev/null +++ b/src/tests/elementLocking.test.tsx @@ -0,0 +1,388 @@ +import ReactDOM from "react-dom"; +import ExcalidrawApp from "../excalidraw-app"; +import { render } from "../tests/test-utils"; +import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; +import { KEYS } from "../keys"; +import { API } from "../tests/helpers/api"; +import { actionSelectAll } from "../actions"; +import { t } from "../i18n"; +import { mutateElement } from "../element/mutateElement"; + +ReactDOM.unmountComponentAtNode(document.getElementById("root")!); + +const mouse = new Pointer("mouse"); +const h = window.h; + +describe("element locking", () => { + beforeEach(async () => { + await render(); + h.elements = []; + }); + + it("click-selecting a locked element is disabled", () => { + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + }); + + h.elements = [lockedRectangle]; + + mouse.clickAt(50, 50); + expect(API.getSelectedElements().length).toBe(0); + }); + + it("box-selecting a locked element is disabled", () => { + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + x: 100, + y: 100, + }); + + h.elements = [lockedRectangle]; + + mouse.downAt(50, 50); + mouse.moveTo(250, 250); + mouse.upAt(250, 250); + expect(API.getSelectedElements().length).toBe(0); + }); + + it("dragging a locked element is disabled", () => { + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + }); + + h.elements = [lockedRectangle]; + + mouse.downAt(50, 50); + mouse.moveTo(100, 100); + mouse.upAt(100, 100); + expect(lockedRectangle).toEqual(expect.objectContaining({ x: 0, y: 0 })); + }); + + it("you can drag element that's below a locked element", () => { + const rectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + }); + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + }); + + h.elements = [rectangle, lockedRectangle]; + + mouse.downAt(50, 50); + mouse.moveTo(100, 100); + mouse.upAt(100, 100); + expect(lockedRectangle).toEqual(expect.objectContaining({ x: 0, y: 0 })); + expect(rectangle).toEqual(expect.objectContaining({ x: 50, y: 50 })); + expect(API.getSelectedElements().length).toBe(1); + expect(API.getSelectedElement().id).toBe(rectangle.id); + }); + + it("selectAll shouldn't select locked elements", () => { + h.elements = [ + API.createElement({ type: "rectangle" }), + API.createElement({ type: "rectangle", locked: true }), + ]; + h.app.actionManager.executeAction(actionSelectAll); + expect(API.getSelectedElements().length).toBe(1); + }); + + it("clicking on a locked element should select the unlocked element beneath it", () => { + const rectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + }); + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + }); + + h.elements = [rectangle, lockedRectangle]; + expect(API.getSelectedElements().length).toBe(0); + mouse.clickAt(50, 50); + expect(API.getSelectedElements().length).toBe(1); + expect(API.getSelectedElement().id).toBe(rectangle.id); + }); + + it("right-clicking on a locked element should select it & open its contextMenu", () => { + const rectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + }); + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + }); + + h.elements = [rectangle, lockedRectangle]; + expect(API.getSelectedElements().length).toBe(0); + mouse.rightClickAt(50, 50); + expect(API.getSelectedElements().length).toBe(1); + expect(API.getSelectedElement().id).toBe(lockedRectangle.id); + + const contextMenu = UI.queryContextMenu(); + expect(contextMenu).not.toBeNull(); + expect( + contextMenu?.querySelector( + `li[data-testid="toggleLock"] .context-menu-option__label`, + ), + ).toHaveTextContent(t("labels.elementLock.unlock")); + }); + + it("right-clicking on element covered by locked element should ignore the locked element", () => { + const rectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + }); + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + }); + + h.elements = [rectangle, lockedRectangle]; + API.setSelectedElements([rectangle]); + expect(API.getSelectedElements().length).toBe(1); + expect(API.getSelectedElement().id).toBe(rectangle.id); + mouse.rightClickAt(50, 50); + expect(API.getSelectedElements().length).toBe(1); + expect(API.getSelectedElement().id).toBe(rectangle.id); + + const contextMenu = UI.queryContextMenu(); + expect(contextMenu).not.toBeNull(); + }); + + it("selecting a group selects all elements including locked ones", () => { + const rectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + groupIds: ["g1"], + }); + const lockedRectangle = API.createElement({ + type: "rectangle", + width: 100, + backgroundColor: "red", + fillStyle: "solid", + locked: true, + groupIds: ["g1"], + x: 200, + y: 200, + }); + + h.elements = [rectangle, lockedRectangle]; + + mouse.clickAt(250, 250); + expect(API.getSelectedElements().length).toBe(0); + + mouse.clickAt(50, 50); + expect(API.getSelectedElements().length).toBe(2); + }); + + it("should ignore locked text element in center of container on ENTER", () => { + const container = API.createElement({ + type: "rectangle", + width: 100, + }); + const textSize = 20; + const text = API.createElement({ + type: "text", + text: "ola", + x: container.width / 2 - textSize / 2, + y: container.height / 2 - textSize / 2, + width: textSize, + height: textSize, + containerId: container.id, + locked: true, + }); + h.elements = [container, text]; + API.setSelectedElements([container]); + Keyboard.keyPress(KEYS.ENTER); + expect(h.state.editingElement?.id).not.toBe(text.id); + expect(h.state.editingElement?.id).toBe(h.elements[2].id); + }); + + it("should ignore locked text under cursor when clicked with text tool", () => { + const text = API.createElement({ + type: "text", + text: "ola", + x: 60, + y: 0, + width: 100, + height: 100, + locked: true, + }); + h.elements = [text]; + UI.clickTool("text"); + mouse.clickAt(text.x + 50, text.y + 50); + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + expect(editor).not.toBe(null); + expect(h.state.editingElement?.id).not.toBe(text.id); + expect(h.elements.length).toBe(2); + expect(h.state.editingElement?.id).toBe(h.elements[1].id); + }); + + it("should ignore text under cursor when double-clicked with selection tool", () => { + const text = API.createElement({ + type: "text", + text: "ola", + x: 60, + y: 0, + width: 100, + height: 100, + locked: true, + }); + h.elements = [text]; + UI.clickTool("selection"); + mouse.doubleClickAt(text.x + 50, text.y + 50); + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + expect(editor).not.toBe(null); + expect(h.state.editingElement?.id).not.toBe(text.id); + expect(h.elements.length).toBe(2); + expect(h.state.editingElement?.id).toBe(h.elements[1].id); + }); + + it("locking should include bound text", () => { + const container = API.createElement({ + type: "rectangle", + width: 100, + }); + const textSize = 20; + const text = API.createElement({ + type: "text", + text: "ola", + x: container.width / 2 - textSize / 2, + y: container.height / 2 - textSize / 2, + width: textSize, + height: textSize, + containerId: container.id, + }); + mutateElement(container, { + boundElements: [{ id: text.id, type: "text" }], + }); + + h.elements = [container, text]; + + UI.clickTool("selection"); + mouse.clickAt(container.x + 10, container.y + 10); + Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { + Keyboard.keyPress(KEYS.L); + }); + + expect(h.elements).toEqual([ + expect.objectContaining({ + id: container.id, + locked: true, + }), + expect.objectContaining({ + id: text.id, + locked: true, + }), + ]); + }); + + it("bound text shouldn't be editable via double-click", () => { + const container = API.createElement({ + type: "rectangle", + width: 100, + locked: true, + }); + const textSize = 20; + const text = API.createElement({ + type: "text", + text: "ola", + x: container.width / 2 - textSize / 2, + y: container.height / 2 - textSize / 2, + width: textSize, + height: textSize, + containerId: container.id, + locked: true, + }); + mutateElement(container, { + boundElements: [{ id: text.id, type: "text" }], + }); + h.elements = [container, text]; + + UI.clickTool("selection"); + mouse.doubleClickAt(container.width / 2, container.height / 2); + + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + expect(editor).not.toBe(null); + expect(h.state.editingElement?.id).not.toBe(text.id); + expect(h.elements.length).toBe(3); + expect(h.state.editingElement?.id).toBe(h.elements[2].id); + }); + + it("bound text shouldn't be editable via text tool", () => { + const container = API.createElement({ + type: "rectangle", + width: 100, + locked: true, + }); + const textSize = 20; + const text = API.createElement({ + type: "text", + text: "ola", + x: container.width / 2 - textSize / 2, + y: container.height / 2 - textSize / 2, + width: textSize, + height: textSize, + containerId: container.id, + locked: true, + }); + mutateElement(container, { + boundElements: [{ id: text.id, type: "text" }], + }); + h.elements = [container, text]; + + UI.clickTool("text"); + mouse.clickAt(container.width / 2, container.height / 2); + + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + expect(editor).not.toBe(null); + expect(h.state.editingElement?.id).not.toBe(text.id); + expect(h.elements.length).toBe(3); + expect(h.state.editingElement?.id).toBe(h.elements[2].id); + }); +}); diff --git a/src/tests/fixtures/elementFixture.ts b/src/tests/fixtures/elementFixture.ts index eca71256d1..ea5980ca1f 100644 --- a/src/tests/fixtures/elementFixture.ts +++ b/src/tests/fixtures/elementFixture.ts @@ -23,6 +23,7 @@ const elementBase: Omit = { boundElements: null, updated: 1, link: null, + locked: false, }; export const rectangleFixture: ExcalidrawElement = { diff --git a/src/tests/helpers/api.ts b/src/tests/helpers/api.ts index 62f9e92836..8944bfdb0b 100644 --- a/src/tests/helpers/api.ts +++ b/src/tests/helpers/api.ts @@ -15,6 +15,7 @@ import path from "path"; import { getMimeType } from "../../data/blob"; import { newFreeDrawElement } from "../../element/newElement"; import { Point } from "../../types"; +import { getSelectedElements } from "../../scene/selection"; const readFile = util.promisify(fs.readFile); @@ -30,10 +31,10 @@ export class API { }); }; - static getSelectedElements = (): ExcalidrawElement[] => { - return h.elements.filter( - (element) => h.state.selectedElementIds[element.id], - ); + static getSelectedElements = ( + includeBoundTextElement: boolean = false, + ): ExcalidrawElement[] => { + return getSelectedElements(h.elements, h.state, includeBoundTextElement); }; static getSelectedElement = (): ExcalidrawElement => { @@ -100,6 +101,7 @@ export class API { ? ExcalidrawTextElement["containerId"] : never; points?: T extends "arrow" | "line" ? readonly Point[] : never; + locked?: boolean; }): T extends "arrow" | "line" ? ExcalidrawLinearElement : T extends "freedraw" @@ -125,6 +127,7 @@ export class API { roughness: rest.roughness ?? appState.currentItemRoughness, opacity: rest.opacity ?? appState.currentItemOpacity, boundElements: rest.boundElements ?? null, + locked: rest.locked ?? false, }; switch (type) { case "rectangle": diff --git a/src/tests/helpers/ui.ts b/src/tests/helpers/ui.ts index 2430778038..a8b284d72e 100644 --- a/src/tests/helpers/ui.ts +++ b/src/tests/helpers/ui.ts @@ -179,6 +179,14 @@ export class Pointer { this.upAt(); } + rightClickAt(x: number, y: number) { + fireEvent.contextMenu(GlobalTestState.canvas, { + button: 2, + clientX: x, + clientY: y, + }); + } + doubleClickAt(x: number, y: number) { this.moveTo(x, y); fireEvent.doubleClick(GlobalTestState.canvas, this.getEvent()); @@ -309,4 +317,10 @@ export class UI { Keyboard.codePress(CODES.G); }); } + + static queryContextMenu = () => { + return GlobalTestState.renderResult.container.querySelector( + ".context-menu", + ); + }; } diff --git a/src/tests/scene/__snapshots__/export.test.ts.snap b/src/tests/scene/__snapshots__/export.test.ts.snap index c0290f7570..61eb9d7043 100644 --- a/src/tests/scene/__snapshots__/export.test.ts.snap +++ b/src/tests/scene/__snapshots__/export.test.ts.snap @@ -93,7 +93,7 @@ exports[`exportToSvg with elements that have a link 1`] = ` exports[`exportToSvg with exportEmbedScene 1`] = ` " - eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1STU9cdTAwMDMhXHUwMDEwvfdXbPDapLtrv+ytWmNMjFx1MDAxZXpoovFAl9mFlFx1MDAwMlx1MDAwNbZcdTAwMWZp+t9cdTAwMDXaLrrx5lVcdTAwMGUk83hvZph5x06SIHtQgCZcdIJ9gTkjXHUwMDFh71DX41vQhknhnvJcdTAwMTBcdTAwMWJZ61wiMKm1atLrcelcdTAwMDRUXHUwMDFhe+ZcdTAwMDOHNVxia1x1MDAxY+PDxUlyXGa3e2HEq7ZcdTAwMGK9eZuWKyZIvinWo5fZ9Ok9SFx1MDAwM2nvOP2s38RcdTAwMDdf+HbUxDtGLHVYlqZcckaBVdS2QCwq7tuMiLFaruBBcql9IzdpOLH0XHUwMDEyXHUwMDE3q0rLWpDIyVx1MDAwNlx1MDAxOC/LyClcdTAwMTnnc3vg51x1MDAwMeCC1lx1MDAxYVCrwuLaYlx1MDAwYm90RrpcdTAwMDFHlStZUVx1MDAwMcb80EiFXHUwMDBiZlx1MDAwZq1f+f7UM1x00/1s56dYq0tcdTAwMWVkfPCtM1x1MDAwMFx1MDAxMlL1s+FgdJeOm5e43yxP2+irXHUwMDE0YddZNlx1MDAxZadpP1x1MDAxZlxyXHUwMDFiXHUwMDA2MzO3alx1MDAxYtKWmFx1MDAxYohz9CN8jDZcdTAwMTA1581jrVxiPoviVzlcdTAwMTOrNu9qR8LwWlxuglx1MDAwMn7q/jvq31F/dFx1MDAxNHDOlIGLo9xcdTAwMWR+jbBSc+tcdTAwMTI5ytlfaMtgd//LXHUwMDA2y3C8PvjRb1x1MDAxMHxXx1Pn9Fx1MDAwNbeWWs0ifQ== + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1SPW/CMFx1MDAxMN35XHUwMDE1kbtcIpGk4aNstFRVpapcdTAwMWRcdTAwMTiQWnUw8YVYMbaxXHUwMDFkPoT477VccsRtxNpcclx1MDAwZpbu+b278907dKJcYpm9XHUwMDA0NI5cdTAwMTDscswoUXiLulx1MDAwZd+A0lRw+5T6WIta5Z5ZXHUwMDFhI8e9XHUwMDFlXHUwMDEzVlBcbm1OfGCwXHUwMDAybrRlfNk4ilx1MDAwZf62L5Q41Wau1lx1MDAxZpOiopyk63w1fJtOXj691JN2lpMlWVx1MDAxM+9d4fthXHUwMDEzbykxpcWSOG6wXHUwMDEy6LI0LVx1MDAxMPMlc21cdTAwMDZEXHUwMDFiJSp4XHUwMDEyTCjXyF3sTyi9wHm1VKLmJHCSPsaLXCJwXG7K2Mzs2WlcdTAwMDA4L2tcdTAwMDWoVWF+abGFNzot7ICDypZcXJZcdTAwMWO0/qNcdTAwMTFcdTAwMTLn1Oxbv3L9yVfip/vdzl9iJc95kHbBr85cdTAwMDCIT5Ulg/7wIVx1MDAxZTUvYb9JXHUwMDFht9F3wf2uk2Q0iuMsXHUwMDFkXHUwMDBlXHUwMDFhXHUwMDA21VO7auPTXHUwMDE2mGlcYnN0I3xcdTAwMGU24DVjzWMtXHQ+icJXXHUwMDE55VWbZ11VXcl9cSmheCU4QVx1MDAxZT92b0a7XHUwMDE57X+MXHUwMDA2jFGp4Ww0e/thICzlzNj8lnKyXHUwMDFk2lDYPl5ZbOGP03ubusWCa/Zw7Fx1MDAxY39cdTAwMDCLqmbvIn0=