mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-27 19:29:55 +02:00
fix: editing linear element (#9839)
This commit is contained in:
@@ -2,7 +2,6 @@ import {
|
||||
arrayToMap,
|
||||
arrayToObject,
|
||||
assertNever,
|
||||
invariant,
|
||||
isDevEnv,
|
||||
isShallowEqual,
|
||||
isTestEnv,
|
||||
@@ -561,70 +560,50 @@ export class AppStateDelta implements DeltaContainer<AppState> {
|
||||
): [AppState, boolean] {
|
||||
try {
|
||||
const {
|
||||
selectedElementIds: removedSelectedElementIds = {},
|
||||
selectedGroupIds: removedSelectedGroupIds = {},
|
||||
selectedElementIds: deletedSelectedElementIds = {},
|
||||
selectedGroupIds: deletedSelectedGroupIds = {},
|
||||
} = this.delta.deleted;
|
||||
|
||||
const {
|
||||
selectedElementIds: addedSelectedElementIds = {},
|
||||
selectedGroupIds: addedSelectedGroupIds = {},
|
||||
selectedLinearElementId,
|
||||
selectedLinearElementIsEditing,
|
||||
selectedElementIds: insertedSelectedElementIds = {},
|
||||
selectedGroupIds: insertedSelectedGroupIds = {},
|
||||
selectedLinearElement: insertedSelectedLinearElement,
|
||||
...directlyApplicablePartial
|
||||
} = this.delta.inserted;
|
||||
|
||||
const mergedSelectedElementIds = Delta.mergeObjects(
|
||||
appState.selectedElementIds,
|
||||
addedSelectedElementIds,
|
||||
removedSelectedElementIds,
|
||||
insertedSelectedElementIds,
|
||||
deletedSelectedElementIds,
|
||||
);
|
||||
|
||||
const mergedSelectedGroupIds = Delta.mergeObjects(
|
||||
appState.selectedGroupIds,
|
||||
addedSelectedGroupIds,
|
||||
removedSelectedGroupIds,
|
||||
insertedSelectedGroupIds,
|
||||
deletedSelectedGroupIds,
|
||||
);
|
||||
|
||||
let selectedLinearElement = appState.selectedLinearElement;
|
||||
|
||||
if (selectedLinearElementId === null) {
|
||||
// Unselect linear element (visible change)
|
||||
selectedLinearElement = null;
|
||||
} else if (
|
||||
selectedLinearElementId &&
|
||||
nextElements.has(selectedLinearElementId)
|
||||
) {
|
||||
selectedLinearElement = new LinearElementEditor(
|
||||
nextElements.get(
|
||||
selectedLinearElementId,
|
||||
) as NonDeleted<ExcalidrawLinearElement>,
|
||||
nextElements,
|
||||
selectedLinearElementIsEditing === true, // Can be unknown which is defaulted to false
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
// Value being 'null' is equivaluent to unknown in this case because it only gets set
|
||||
// to null when 'selectedLinearElementId' is set to null
|
||||
selectedLinearElementIsEditing != null
|
||||
) {
|
||||
invariant(
|
||||
selectedLinearElement,
|
||||
`selectedLinearElement is null when selectedLinearElementIsEditing is set to ${selectedLinearElementIsEditing}`,
|
||||
);
|
||||
|
||||
selectedLinearElement = {
|
||||
...selectedLinearElement,
|
||||
isEditing: selectedLinearElementIsEditing,
|
||||
};
|
||||
}
|
||||
const selectedLinearElement =
|
||||
insertedSelectedLinearElement &&
|
||||
nextElements.has(insertedSelectedLinearElement.elementId)
|
||||
? new LinearElementEditor(
|
||||
nextElements.get(
|
||||
insertedSelectedLinearElement.elementId,
|
||||
) as NonDeleted<ExcalidrawLinearElement>,
|
||||
nextElements,
|
||||
insertedSelectedLinearElement.isEditing,
|
||||
)
|
||||
: null;
|
||||
|
||||
const nextAppState = {
|
||||
...appState,
|
||||
...directlyApplicablePartial,
|
||||
selectedElementIds: mergedSelectedElementIds,
|
||||
selectedGroupIds: mergedSelectedGroupIds,
|
||||
selectedLinearElement,
|
||||
selectedLinearElement:
|
||||
typeof insertedSelectedLinearElement !== "undefined"
|
||||
? selectedLinearElement
|
||||
: appState.selectedLinearElement,
|
||||
};
|
||||
|
||||
const constainsVisibleChanges = this.filterInvisibleChanges(
|
||||
@@ -753,64 +732,53 @@ export class AppStateDelta implements DeltaContainer<AppState> {
|
||||
}
|
||||
|
||||
break;
|
||||
case "selectedLinearElementId": {
|
||||
const appStateKey = AppStateDelta.convertToAppStateKey(key);
|
||||
const linearElement = nextAppState[appStateKey];
|
||||
case "selectedLinearElement":
|
||||
const nextLinearElement = nextAppState[key];
|
||||
|
||||
if (!linearElement) {
|
||||
if (!nextLinearElement) {
|
||||
// previously there was a linear element (assuming visible), now there is none
|
||||
visibleDifferenceFlag.value = true;
|
||||
} else {
|
||||
const element = nextElements.get(linearElement.elementId);
|
||||
const element = nextElements.get(nextLinearElement.elementId);
|
||||
|
||||
if (element && !element.isDeleted) {
|
||||
// previously there wasn't a linear element, now there is one which is visible
|
||||
visibleDifferenceFlag.value = true;
|
||||
} else {
|
||||
// there was assigned a linear element now, but it's deleted
|
||||
nextAppState[appStateKey] = null;
|
||||
nextAppState[key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "selectedLinearElementIsEditing": {
|
||||
// Changes in editing state are always visible
|
||||
const prevIsEditing =
|
||||
prevAppState.selectedLinearElement?.isEditing ?? false;
|
||||
const nextIsEditing =
|
||||
nextAppState.selectedLinearElement?.isEditing ?? false;
|
||||
|
||||
if (prevIsEditing !== nextIsEditing) {
|
||||
visibleDifferenceFlag.value = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "lockedMultiSelections": {
|
||||
case "lockedMultiSelections":
|
||||
const prevLockedUnits = prevAppState[key] || {};
|
||||
const nextLockedUnits = nextAppState[key] || {};
|
||||
|
||||
// TODO: this seems wrong, we are already doing this comparison generically above,
|
||||
// hence instead we should check whether elements are actually visible,
|
||||
// so that once these changes are applied they actually result in a visible change to the user
|
||||
if (!isShallowEqual(prevLockedUnits, nextLockedUnits)) {
|
||||
visibleDifferenceFlag.value = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "activeLockedId": {
|
||||
case "activeLockedId":
|
||||
const prevHitLockedId = prevAppState[key] || null;
|
||||
const nextHitLockedId = nextAppState[key] || null;
|
||||
|
||||
// TODO: this seems wrong, we are already doing this comparison generically above,
|
||||
// hence instead we should check whether elements are actually visible,
|
||||
// so that once these changes are applied they actually result in a visible change to the user
|
||||
if (prevHitLockedId !== nextHitLockedId) {
|
||||
visibleDifferenceFlag.value = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
default:
|
||||
assertNever(
|
||||
key,
|
||||
`Unknown ObservedElementsAppState's key "${key}"`,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -818,15 +786,6 @@ export class AppStateDelta implements DeltaContainer<AppState> {
|
||||
return visibleDifferenceFlag.value;
|
||||
}
|
||||
|
||||
private static convertToAppStateKey(
|
||||
key: keyof Pick<ObservedElementsAppState, "selectedLinearElementId">,
|
||||
): keyof Pick<AppState, "selectedLinearElement"> {
|
||||
switch (key) {
|
||||
case "selectedLinearElementId":
|
||||
return "selectedLinearElement";
|
||||
}
|
||||
}
|
||||
|
||||
private static filterSelectedElements(
|
||||
selectedElementIds: AppState["selectedElementIds"],
|
||||
elements: SceneElementsMap,
|
||||
@@ -891,8 +850,7 @@ export class AppStateDelta implements DeltaContainer<AppState> {
|
||||
editingGroupId,
|
||||
selectedGroupIds,
|
||||
selectedElementIds,
|
||||
selectedLinearElementId,
|
||||
selectedLinearElementIsEditing,
|
||||
selectedLinearElement,
|
||||
croppingElementId,
|
||||
lockedMultiSelections,
|
||||
activeLockedId,
|
||||
|
@@ -978,8 +978,7 @@ const getDefaultObservedAppState = (): ObservedAppState => {
|
||||
viewBackgroundColor: COLOR_PALETTE.white,
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
selectedLinearElementId: null,
|
||||
selectedLinearElementIsEditing: null,
|
||||
selectedLinearElement: null,
|
||||
croppingElementId: null,
|
||||
activeLockedId: null,
|
||||
lockedMultiSelections: {},
|
||||
@@ -998,14 +997,12 @@ export const getObservedAppState = (
|
||||
croppingElementId: appState.croppingElementId,
|
||||
activeLockedId: appState.activeLockedId,
|
||||
lockedMultiSelections: appState.lockedMultiSelections,
|
||||
selectedLinearElementId:
|
||||
(appState as AppState).selectedLinearElement?.elementId ??
|
||||
(appState as ObservedAppState).selectedLinearElementId ??
|
||||
null,
|
||||
selectedLinearElementIsEditing:
|
||||
(appState as AppState).selectedLinearElement?.isEditing ??
|
||||
(appState as ObservedAppState).selectedLinearElementIsEditing ??
|
||||
null,
|
||||
selectedLinearElement: appState.selectedLinearElement
|
||||
? {
|
||||
elementId: appState.selectedLinearElement.elementId,
|
||||
isEditing: !!appState.selectedLinearElement.isEditing,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
||||
Reflect.defineProperty(observedAppState, hiddenObservedAppStateProp, {
|
||||
|
@@ -7,7 +7,10 @@ describe("AppStateDelta", () => {
|
||||
describe("ensure stable delta properties order", () => {
|
||||
it("should maintain stable order for root properties", () => {
|
||||
const name = "untitled scene";
|
||||
const selectedLinearElementId = "id1" as LinearElementEditor["elementId"];
|
||||
const selectedLinearElement = {
|
||||
elementId: "id1" as LinearElementEditor["elementId"],
|
||||
isEditing: false,
|
||||
};
|
||||
|
||||
const commonAppState = {
|
||||
viewBackgroundColor: "#ffffff",
|
||||
@@ -24,23 +27,23 @@ describe("AppStateDelta", () => {
|
||||
const prevAppState1: ObservedAppState = {
|
||||
...commonAppState,
|
||||
name: "",
|
||||
selectedLinearElementId: null,
|
||||
selectedLinearElement: null,
|
||||
};
|
||||
|
||||
const nextAppState1: ObservedAppState = {
|
||||
...commonAppState,
|
||||
name,
|
||||
selectedLinearElementId,
|
||||
selectedLinearElement,
|
||||
};
|
||||
|
||||
const prevAppState2: ObservedAppState = {
|
||||
selectedLinearElementId: null,
|
||||
selectedLinearElement: null,
|
||||
name: "",
|
||||
...commonAppState,
|
||||
};
|
||||
|
||||
const nextAppState2: ObservedAppState = {
|
||||
selectedLinearElementId,
|
||||
selectedLinearElement,
|
||||
name,
|
||||
...commonAppState,
|
||||
};
|
||||
@@ -58,9 +61,7 @@ describe("AppStateDelta", () => {
|
||||
selectedGroupIds: {},
|
||||
editingGroupId: null,
|
||||
croppingElementId: null,
|
||||
selectedLinearElementId: null,
|
||||
selectedLinearElementIsEditing: null,
|
||||
editingLinearElementId: null,
|
||||
selectedLinearElement: null,
|
||||
activeLockedId: null,
|
||||
lockedMultiSelections: {},
|
||||
};
|
||||
@@ -106,9 +107,7 @@ describe("AppStateDelta", () => {
|
||||
selectedElementIds: {},
|
||||
editingGroupId: null,
|
||||
croppingElementId: null,
|
||||
selectedLinearElementId: null,
|
||||
selectedLinearElementIsEditing: null,
|
||||
editingLinearElementId: null,
|
||||
selectedLinearElement: null,
|
||||
activeLockedId: null,
|
||||
lockedMultiSelections: {},
|
||||
};
|
||||
|
@@ -20,7 +20,7 @@ export type ReconciledExcalidrawElement = OrderedExcalidrawElement &
|
||||
export type RemoteExcalidrawElement = OrderedExcalidrawElement &
|
||||
MakeBrand<"RemoteExcalidrawElement">;
|
||||
|
||||
const shouldDiscardRemoteElement = (
|
||||
export const shouldDiscardRemoteElement = (
|
||||
localAppState: AppState,
|
||||
local: OrderedExcalidrawElement | undefined,
|
||||
remote: RemoteExcalidrawElement,
|
||||
@@ -30,7 +30,7 @@ const shouldDiscardRemoteElement = (
|
||||
// local element is being edited
|
||||
(local.id === localAppState.editingTextElement?.id ||
|
||||
local.id === localAppState.resizingElement?.id ||
|
||||
local.id === localAppState.newElement?.id || // TODO: Is this still valid? As newElement is selection element, which is never part of the elements array
|
||||
local.id === localAppState.newElement?.id ||
|
||||
// local element is newer
|
||||
local.version > remote.version ||
|
||||
// resolve conflicting edits deterministically by taking the one with
|
||||
|
@@ -291,6 +291,7 @@ const restoreElement = (
|
||||
// if empty text, mark as deleted. We keep in array
|
||||
// for data integrity purposes (collab etc.)
|
||||
if (!text && !element.isDeleted) {
|
||||
// TODO: we should not do this since it breaks sync / versioning when we exchange / apply just deltas and restore the elements (deletion isn't recorded)
|
||||
element = { ...element, originalText: text, isDeleted: true };
|
||||
element = bumpVersion(element);
|
||||
}
|
||||
|
@@ -543,13 +543,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||
"selectedElementIds": {
|
||||
"id4": true,
|
||||
},
|
||||
"selectedLinearElementId": "id4",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id4",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1026,13 +1027,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||
"selectedElementIds": {
|
||||
"id4": true,
|
||||
},
|
||||
"selectedLinearElementId": "id4",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id4",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2414,13 +2416,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||
"selectedElementIds": {
|
||||
"id4": true,
|
||||
},
|
||||
"selectedLinearElementId": "id4",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id4",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -7028,9 +7031,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
|
||||
"scrollX": 0,
|
||||
"scrollY": 0,
|
||||
"searchMatches": null,
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedElementIds": {},
|
||||
"selectedElementsAreBeingDragged": false,
|
||||
"selectedGroupIds": {},
|
||||
"selectionElement": null,
|
||||
@@ -7131,74 +7132,70 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
|
||||
},
|
||||
"elements": {
|
||||
"added": {},
|
||||
"removed": {
|
||||
"id0": {
|
||||
"deleted": {
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"customData": undefined,
|
||||
"elbowed": false,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 10,
|
||||
"index": "a0",
|
||||
"isDeleted": false,
|
||||
"lastCommittedPoint": [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"opacity": 100,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
],
|
||||
[
|
||||
10,
|
||||
10,
|
||||
],
|
||||
],
|
||||
"roughness": 1,
|
||||
"roundness": {
|
||||
"type": 2,
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"version": 6,
|
||||
"width": 10,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"removed": {},
|
||||
"updated": {},
|
||||
},
|
||||
"id": "id13",
|
||||
},
|
||||
{
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
"inserted": {
|
||||
"isDeleted": true,
|
||||
"version": 5,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
"elements": {
|
||||
"added": {},
|
||||
"removed": {},
|
||||
"updated": {},
|
||||
},
|
||||
"id": "id14",
|
||||
},
|
||||
{
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": true,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"elements": {
|
||||
"added": {},
|
||||
"removed": {},
|
||||
"updated": {},
|
||||
},
|
||||
"id": "id2",
|
||||
"id": "id15",
|
||||
},
|
||||
{
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -7207,43 +7204,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
|
||||
"removed": {},
|
||||
"updated": {},
|
||||
},
|
||||
"id": "id4",
|
||||
},
|
||||
{
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementIsEditing": true,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementIsEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"elements": {
|
||||
"added": {},
|
||||
"removed": {},
|
||||
"updated": {},
|
||||
},
|
||||
"id": "id6",
|
||||
},
|
||||
{
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementIsEditing": false,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementIsEditing": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"elements": {
|
||||
"added": {},
|
||||
"removed": {},
|
||||
"updated": {},
|
||||
},
|
||||
"id": "id10",
|
||||
"id": "id16",
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -10210,12 +10171,13 @@ exports[`history > multiplayer undo/redo > should override remotely added points
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -15770,13 +15732,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -16064,15 +16027,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -16688,15 +16652,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -17320,15 +17285,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -18017,15 +17983,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -18132,15 +18099,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -18744,15 +18712,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -18859,15 +18828,16 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id13": true,
|
||||
},
|
||||
"selectedLinearElementId": "id13",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id13",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -20656,12 +20626,13 @@ exports[`history > singleplayer undo/redo > should support linear element creati
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -20676,10 +20647,16 @@ exports[`history > singleplayer undo/redo > should support linear element creati
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementIsEditing": true,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": true,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -20747,10 +20724,16 @@ exports[`history > singleplayer undo/redo > should support linear element creati
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementIsEditing": true,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -6394,15 +6394,16 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"selectedElementIds": {
|
||||
"id9": true,
|
||||
},
|
||||
"selectedLinearElementId": "id9",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id9",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id6": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6470,13 +6471,19 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"selectedElementIds": {
|
||||
"id12": true,
|
||||
},
|
||||
"selectedLinearElementId": "id12",
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id12",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id9": true,
|
||||
},
|
||||
"selectedLinearElementId": "id9",
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id9",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6542,15 +6549,16 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"selectedElementIds": {
|
||||
"id15": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id12": true,
|
||||
},
|
||||
"selectedLinearElementId": "id12",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id12",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6677,12 +6685,13 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": "id15",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id15",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6700,15 +6709,16 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"selectedElementIds": {
|
||||
"id22": true,
|
||||
},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {
|
||||
"id15": true,
|
||||
},
|
||||
"selectedLinearElementId": "id15",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id15",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6833,12 +6843,13 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": "id22",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id22",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6873,12 +6884,13 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": "id22",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id22",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -8719,13 +8731,14 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] undo stack
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -8946,13 +8959,14 @@ exports[`regression tests > key 6 selects line tool > [end of test] undo stack 1
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -9365,13 +9379,14 @@ exports[`regression tests > key a selects arrow tool > [end of test] undo stack
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -9770,13 +9785,14 @@ exports[`regression tests > key l selects line tool > [end of test] undo stack 1
|
||||
"selectedElementIds": {
|
||||
"id0": true,
|
||||
},
|
||||
"selectedLinearElementId": "id0",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id0",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
"inserted": {
|
||||
"selectedElementIds": {},
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -14494,12 +14510,13 @@ exports[`regression tests > undo/redo drawing an element > [end of test] redo st
|
||||
"appState": AppStateDelta {
|
||||
"delta": Delta {
|
||||
"deleted": {
|
||||
"selectedLinearElementId": null,
|
||||
"selectedLinearElementIsEditing": null,
|
||||
"selectedLinearElement": null,
|
||||
},
|
||||
"inserted": {
|
||||
"selectedLinearElementId": "id6",
|
||||
"selectedLinearElementIsEditing": false,
|
||||
"selectedLinearElement": {
|
||||
"elementId": "id6",
|
||||
"isEditing": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -3043,15 +3043,14 @@ describe("history", () => {
|
||||
});
|
||||
|
||||
Keyboard.undo();
|
||||
expect(API.getUndoStack().length).toBe(3);
|
||||
expect(API.getRedoStack().length).toBe(1);
|
||||
expect(h.state.selectedLinearElement).not.toBeNull();
|
||||
expect(h.state.selectedLinearElement?.isEditing).toBe(true);
|
||||
expect(API.getUndoStack().length).toBe(0);
|
||||
expect(API.getRedoStack().length).toBe(4);
|
||||
expect(h.state.selectedLinearElement).toBeNull();
|
||||
|
||||
Keyboard.redo();
|
||||
expect(API.getUndoStack().length).toBe(4);
|
||||
expect(API.getRedoStack().length).toBe(0);
|
||||
expect(h.state.selectedLinearElement).not.toBeNull();
|
||||
expect(h.state.selectedLinearElement).toBeNull();
|
||||
expect(h.state.selectedLinearElement?.isEditing ?? false).toBe(false);
|
||||
});
|
||||
|
||||
|
@@ -248,8 +248,10 @@ export type ObservedElementsAppState = {
|
||||
editingGroupId: AppState["editingGroupId"];
|
||||
selectedElementIds: AppState["selectedElementIds"];
|
||||
selectedGroupIds: AppState["selectedGroupIds"];
|
||||
selectedLinearElementId: LinearElementEditor["elementId"] | null;
|
||||
selectedLinearElementIsEditing: boolean | null;
|
||||
selectedLinearElement: {
|
||||
elementId: LinearElementEditor["elementId"];
|
||||
isEditing: boolean;
|
||||
} | null;
|
||||
croppingElementId: AppState["croppingElementId"];
|
||||
lockedMultiSelections: AppState["lockedMultiSelections"];
|
||||
activeLockedId: AppState["activeLockedId"];
|
||||
|
Reference in New Issue
Block a user