Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
027ef1d641 | ||
|
|
d9bbf1eda6 |
+1
-1
@@ -54,7 +54,7 @@
|
|||||||
"vitest-canvas-mock": "0.3.2"
|
"vitest-canvas-mock": "0.3.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18.0.0 - 20.x.x"
|
"node": "18.0.0 - 22.x.x"
|
||||||
},
|
},
|
||||||
"homepage": ".",
|
"homepage": ".",
|
||||||
"prettier": "@excalidraw/prettier-config",
|
"prettier": "@excalidraw/prettier-config",
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import { arrayToMap } from "../utils";
|
|||||||
import { CODES, KEYS } from "../keys";
|
import { CODES, KEYS } from "../keys";
|
||||||
import { getCommonBoundingBox } from "../element/bounds";
|
import { getCommonBoundingBox } from "../element/bounds";
|
||||||
import {
|
import {
|
||||||
bindOrUnbindSelectedElements,
|
bindOrUnbindLinearElements,
|
||||||
isBindingEnabled,
|
isBindingEnabled,
|
||||||
unbindLinearElements,
|
|
||||||
} from "../element/binding";
|
} from "../element/binding";
|
||||||
import { updateFrameMembershipOfSelectedElements } from "../frame";
|
import { updateFrameMembershipOfSelectedElements } from "../frame";
|
||||||
import { flipHorizontal, flipVertical } from "../components/icons";
|
import { flipHorizontal, flipVertical } from "../components/icons";
|
||||||
import { StoreAction } from "../store";
|
import { StoreAction } from "../store";
|
||||||
|
import { isLinearElement } from "../element/typeChecks";
|
||||||
|
|
||||||
export const actionFlipHorizontal = register({
|
export const actionFlipHorizontal = register({
|
||||||
name: "flipHorizontal",
|
name: "flipHorizontal",
|
||||||
@@ -89,7 +89,6 @@ const flipSelectedElements = (
|
|||||||
|
|
||||||
const updatedElements = flipElements(
|
const updatedElements = flipElements(
|
||||||
selectedElements,
|
selectedElements,
|
||||||
elements,
|
|
||||||
elementsMap,
|
elementsMap,
|
||||||
appState,
|
appState,
|
||||||
flipDirection,
|
flipDirection,
|
||||||
@@ -105,7 +104,6 @@ const flipSelectedElements = (
|
|||||||
|
|
||||||
const flipElements = (
|
const flipElements = (
|
||||||
selectedElements: NonDeleted<ExcalidrawElement>[],
|
selectedElements: NonDeleted<ExcalidrawElement>[],
|
||||||
elements: readonly ExcalidrawElement[],
|
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
flipDirection: "horizontal" | "vertical",
|
flipDirection: "horizontal" | "vertical",
|
||||||
@@ -124,9 +122,12 @@ const flipElements = (
|
|||||||
flipDirection === "horizontal" ? minY : maxY,
|
flipDirection === "horizontal" ? minY : maxY,
|
||||||
);
|
);
|
||||||
|
|
||||||
isBindingEnabled(appState)
|
bindOrUnbindLinearElements(
|
||||||
? bindOrUnbindSelectedElements(selectedElements, app)
|
selectedElements.filter(isLinearElement),
|
||||||
: unbindLinearElements(selectedElements, elementsMap);
|
app,
|
||||||
|
isBindingEnabled(appState),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return selectedElements;
|
return selectedElements;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -122,17 +122,16 @@ import {
|
|||||||
} from "../element";
|
} from "../element";
|
||||||
import {
|
import {
|
||||||
bindOrUnbindLinearElement,
|
bindOrUnbindLinearElement,
|
||||||
bindOrUnbindSelectedElements,
|
bindOrUnbindLinearElements,
|
||||||
fixBindingsAfterDeletion,
|
fixBindingsAfterDeletion,
|
||||||
fixBindingsAfterDuplication,
|
fixBindingsAfterDuplication,
|
||||||
getEligibleElementsForBinding,
|
|
||||||
getHoveredElementForBinding,
|
getHoveredElementForBinding,
|
||||||
isBindingEnabled,
|
isBindingEnabled,
|
||||||
isLinearElementSimpleAndAlreadyBound,
|
isLinearElementSimpleAndAlreadyBound,
|
||||||
maybeBindLinearElement,
|
maybeBindLinearElement,
|
||||||
shouldEnableBindingForPointerEvent,
|
shouldEnableBindingForPointerEvent,
|
||||||
unbindLinearElements,
|
|
||||||
updateBoundElements,
|
updateBoundElements,
|
||||||
|
getSuggestedBindingsForArrows,
|
||||||
} from "../element/binding";
|
} from "../element/binding";
|
||||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||||
import { mutateElement, newElementWith } from "../element/mutateElement";
|
import { mutateElement, newElementWith } from "../element/mutateElement";
|
||||||
@@ -3938,7 +3937,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.maybeSuggestBindingForAll(selectedElements);
|
this.setState({
|
||||||
|
suggestedBindings: getSuggestedBindingsForArrows(
|
||||||
|
selectedElements,
|
||||||
|
this,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (event.key === KEYS.ENTER) {
|
} else if (event.key === KEYS.ENTER) {
|
||||||
@@ -4105,11 +4109,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.setState({ isBindingEnabled: true });
|
this.setState({ isBindingEnabled: true });
|
||||||
}
|
}
|
||||||
if (isArrowKey(event.key)) {
|
if (isArrowKey(event.key)) {
|
||||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
bindOrUnbindLinearElements(
|
||||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
this.scene.getSelectedElements(this.state).filter(isLinearElement),
|
||||||
isBindingEnabled(this.state)
|
this,
|
||||||
? bindOrUnbindSelectedElements(selectedElements, this)
|
isBindingEnabled(this.state),
|
||||||
: unbindLinearElements(selectedElements, elementsMap);
|
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
|
||||||
|
);
|
||||||
this.setState({ suggestedBindings: [] });
|
this.setState({ suggestedBindings: [] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -7453,7 +7458,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize,
|
event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.maybeSuggestBindingForAll(selectedElements);
|
this.setState({
|
||||||
|
suggestedBindings: getSuggestedBindingsForArrows(
|
||||||
|
selectedElements,
|
||||||
|
this,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
// We duplicate the selected element if alt is pressed on pointer move
|
// We duplicate the selected element if alt is pressed on pointer move
|
||||||
if (event.altKey && !pointerDownState.hit.hasBeenDuplicated) {
|
if (event.altKey && !pointerDownState.hit.hasBeenDuplicated) {
|
||||||
@@ -8500,15 +8510,18 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
|
if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
|
||||||
isBindingEnabled(this.state)
|
// We only allow binding via linear elements, specifically via dragging
|
||||||
? bindOrUnbindSelectedElements(
|
// the endpoints ("start" or "end").
|
||||||
this.scene.getSelectedElements(this.state),
|
const linearElements = this.scene
|
||||||
this,
|
.getSelectedElements(this.state)
|
||||||
)
|
.filter(isLinearElement);
|
||||||
: unbindLinearElements(
|
|
||||||
this.scene.getSelectedElements(this.state),
|
bindOrUnbindLinearElements(
|
||||||
elementsMap,
|
linearElements,
|
||||||
);
|
this,
|
||||||
|
isBindingEnabled(this.state),
|
||||||
|
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTool.type === "laser") {
|
if (activeTool.type === "laser") {
|
||||||
@@ -9040,19 +9053,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.setState({ suggestedBindings });
|
this.setState({ suggestedBindings });
|
||||||
};
|
};
|
||||||
|
|
||||||
private maybeSuggestBindingForAll(
|
|
||||||
selectedElements: NonDeleted<ExcalidrawElement>[],
|
|
||||||
): void {
|
|
||||||
if (selectedElements.length > 50) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const suggestedBindings = getEligibleElementsForBinding(
|
|
||||||
selectedElements,
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
this.setState({ suggestedBindings });
|
|
||||||
}
|
|
||||||
|
|
||||||
private clearSelection(hitElement: ExcalidrawElement | null): void {
|
private clearSelection(hitElement: ExcalidrawElement | null): void {
|
||||||
this.setState((prevState) => ({
|
this.setState((prevState) => ({
|
||||||
selectedElementIds: makeNextSelectedElementIds({}, prevState),
|
selectedElementIds: makeNextSelectedElementIds({}, prevState),
|
||||||
@@ -9439,8 +9439,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.state.originSnapOffset,
|
this.state.originSnapOffset,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.maybeSuggestBindingForAll([draggingElement]);
|
|
||||||
|
|
||||||
// highlight elements that are to be added to frames on frames creation
|
// highlight elements that are to be added to frames on frames creation
|
||||||
if (
|
if (
|
||||||
this.state.activeTool.type === TOOL_TYPE.frame ||
|
this.state.activeTool.type === TOOL_TYPE.frame ||
|
||||||
@@ -9563,7 +9561,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
pointerDownState.resize.center.y,
|
pointerDownState.resize.center.y,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
this.maybeSuggestBindingForAll(selectedElements);
|
const suggestedBindings = getSuggestedBindingsForArrows(
|
||||||
|
selectedElements,
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
|
||||||
const elementsToHighlight = new Set<ExcalidrawElement>();
|
const elementsToHighlight = new Set<ExcalidrawElement>();
|
||||||
selectedFrames.forEach((frame) => {
|
selectedFrames.forEach((frame) => {
|
||||||
@@ -9577,6 +9578,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
elementsToHighlight: [...elementsToHighlight],
|
elementsToHighlight: [...elementsToHighlight],
|
||||||
|
suggestedBindings,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -131,73 +131,193 @@ const bindOrUnbindLinearElementEdge = (
|
|||||||
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
if (bindableElement !== "keep") {
|
// "keep" is for method chaining convenience, a "no-op", so just bail out
|
||||||
if (bindableElement != null) {
|
if (bindableElement === "keep") {
|
||||||
// Don't bind if we're trying to bind or are already bound to the same
|
return;
|
||||||
// element on the other edge already ("start" edge takes precedence).
|
}
|
||||||
if (
|
|
||||||
otherEdgeBindableElement == null ||
|
// null means break the bind, so nothing to consider here
|
||||||
(otherEdgeBindableElement === "keep"
|
if (bindableElement === null) {
|
||||||
? !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
|
const unbound = unbindLinearElement(linearElement, startOrEnd);
|
||||||
linearElement,
|
if (unbound != null) {
|
||||||
bindableElement,
|
unboundFromElementIds.add(unbound);
|
||||||
startOrEnd,
|
|
||||||
)
|
|
||||||
: startOrEnd === "start" ||
|
|
||||||
otherEdgeBindableElement.id !== bindableElement.id)
|
|
||||||
) {
|
|
||||||
bindLinearElement(
|
|
||||||
linearElement,
|
|
||||||
bindableElement,
|
|
||||||
startOrEnd,
|
|
||||||
elementsMap,
|
|
||||||
);
|
|
||||||
boundToElementIds.add(bindableElement.id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const unbound = unbindLinearElement(linearElement, startOrEnd);
|
|
||||||
if (unbound != null) {
|
|
||||||
unboundFromElementIds.add(unbound);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// While complext arrows can do anything, simple arrow with both ends trying
|
||||||
|
// to bind to the same bindable should not be allowed, start binding takes
|
||||||
|
// precedence
|
||||||
|
if (isLinearElementSimple(linearElement)) {
|
||||||
|
if (
|
||||||
|
otherEdgeBindableElement == null ||
|
||||||
|
(otherEdgeBindableElement === "keep"
|
||||||
|
? // TODO: Refactor - Needlessly complex
|
||||||
|
!isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
|
||||||
|
linearElement,
|
||||||
|
bindableElement,
|
||||||
|
startOrEnd,
|
||||||
|
)
|
||||||
|
: startOrEnd === "start" ||
|
||||||
|
otherEdgeBindableElement.id !== bindableElement.id)
|
||||||
|
) {
|
||||||
|
bindLinearElement(
|
||||||
|
linearElement,
|
||||||
|
bindableElement,
|
||||||
|
startOrEnd,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
boundToElementIds.add(bindableElement.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bindLinearElement(linearElement, bindableElement, startOrEnd, elementsMap);
|
||||||
|
boundToElementIds.add(bindableElement.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const bindOrUnbindSelectedElements = (
|
const getOriginalBindingIfStillCloseOfLinearElementEdge = (
|
||||||
selectedElements: NonDeleted<ExcalidrawElement>[],
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
edge: "start" | "end",
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
|
): NonDeleted<ExcalidrawElement> | null => {
|
||||||
|
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||||
|
const coors = getLinearElementEdgeCoors(linearElement, edge, elementsMap);
|
||||||
|
const elementId =
|
||||||
|
edge === "start"
|
||||||
|
? linearElement.startBinding?.elementId
|
||||||
|
: linearElement.endBinding?.elementId;
|
||||||
|
if (elementId) {
|
||||||
|
const element = elementsMap.get(
|
||||||
|
elementId,
|
||||||
|
) as NonDeleted<ExcalidrawBindableElement>;
|
||||||
|
if (bindingBorderTest(element, coors, app)) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOriginalBindingsIfStillCloseToArrowEnds = (
|
||||||
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
app: AppClassProperties,
|
||||||
|
): (NonDeleted<ExcalidrawElement> | null)[] =>
|
||||||
|
["start", "end"].map((edge) =>
|
||||||
|
getOriginalBindingIfStillCloseOfLinearElementEdge(
|
||||||
|
linearElement,
|
||||||
|
edge as "start" | "end",
|
||||||
|
app,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const getBindingStrategyForDraggingArrowEndpoints = (
|
||||||
|
selectedElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
isBindingEnabled: boolean,
|
||||||
|
draggingPoints: readonly number[],
|
||||||
|
app: AppClassProperties,
|
||||||
|
): (NonDeleted<ExcalidrawBindableElement> | null | "keep")[] => {
|
||||||
|
const startIdx = 0;
|
||||||
|
const endIdx = selectedElement.points.length - 1;
|
||||||
|
const startDragged = draggingPoints.findIndex((i) => i === startIdx) > -1;
|
||||||
|
const endDragged = draggingPoints.findIndex((i) => i === endIdx) > -1;
|
||||||
|
const start = startDragged
|
||||||
|
? isBindingEnabled
|
||||||
|
? getElligibleElementForBindingElement(selectedElement, "start", app)
|
||||||
|
: null // If binding is disabled and start is dragged, break all binds
|
||||||
|
: // We have to update the focus and gap of the binding, so let's rebind
|
||||||
|
getElligibleElementForBindingElement(selectedElement, "start", app);
|
||||||
|
const end = endDragged
|
||||||
|
? isBindingEnabled
|
||||||
|
? getElligibleElementForBindingElement(selectedElement, "end", app)
|
||||||
|
: null // If binding is disabled and end is dragged, break all binds
|
||||||
|
: // We have to update the focus and gap of the binding, so let's rebind
|
||||||
|
getElligibleElementForBindingElement(selectedElement, "end", app);
|
||||||
|
|
||||||
|
return [start, end];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBindingStrategyForDraggingArrowOrJoints = (
|
||||||
|
selectedElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
app: AppClassProperties,
|
||||||
|
isBindingEnabled: boolean,
|
||||||
|
): (NonDeleted<ExcalidrawBindableElement> | null | "keep")[] => {
|
||||||
|
const [startIsClose, endIsClose] = getOriginalBindingsIfStillCloseToArrowEnds(
|
||||||
|
selectedElement,
|
||||||
|
app,
|
||||||
|
);
|
||||||
|
const start = startIsClose
|
||||||
|
? isBindingEnabled
|
||||||
|
? getElligibleElementForBindingElement(selectedElement, "start", app)
|
||||||
|
: null
|
||||||
|
: null;
|
||||||
|
const end = endIsClose
|
||||||
|
? isBindingEnabled
|
||||||
|
? getElligibleElementForBindingElement(selectedElement, "end", app)
|
||||||
|
: null
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return [start, end];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bindOrUnbindLinearElements = (
|
||||||
|
selectedElements: NonDeleted<ExcalidrawLinearElement>[],
|
||||||
|
app: AppClassProperties,
|
||||||
|
isBindingEnabled: boolean,
|
||||||
|
draggingPoints: readonly number[] | null,
|
||||||
): void => {
|
): void => {
|
||||||
selectedElements.forEach((selectedElement) => {
|
selectedElements.forEach((selectedElement) => {
|
||||||
if (isBindingElement(selectedElement)) {
|
const [start, end] = draggingPoints?.length
|
||||||
bindOrUnbindLinearElement(
|
? // The arrow edge points are dragged (i.e. start, end)
|
||||||
selectedElement,
|
getBindingStrategyForDraggingArrowEndpoints(
|
||||||
getElligibleElementForBindingElement(selectedElement, "start", app),
|
selectedElement,
|
||||||
getElligibleElementForBindingElement(selectedElement, "end", app),
|
isBindingEnabled,
|
||||||
app.scene.getNonDeletedElementsMap(),
|
draggingPoints ?? [],
|
||||||
);
|
app,
|
||||||
} else if (isBindableElement(selectedElement)) {
|
)
|
||||||
maybeBindBindableElement(
|
: // The arrow itself (the shaft) or the inner joins are dragged
|
||||||
selectedElement,
|
getBindingStrategyForDraggingArrowOrJoints(
|
||||||
app.scene.getNonDeletedElementsMap(),
|
selectedElement,
|
||||||
app,
|
app,
|
||||||
);
|
isBindingEnabled,
|
||||||
}
|
);
|
||||||
|
|
||||||
|
bindOrUnbindLinearElement(
|
||||||
|
selectedElement,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const maybeBindBindableElement = (
|
export const getSuggestedBindingsForArrows = (
|
||||||
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
selectedElements: NonDeleted<ExcalidrawElement>[],
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
): void => {
|
): SuggestedBinding[] => {
|
||||||
getElligibleElementsForBindableElementAndWhere(bindableElement, app).forEach(
|
// HOT PATH: Bail out if selected elements list is too large
|
||||||
([linearElement, where]) =>
|
if (selectedElements.length > 50) {
|
||||||
bindOrUnbindLinearElement(
|
return [];
|
||||||
linearElement,
|
}
|
||||||
where === "end" ? "keep" : bindableElement,
|
|
||||||
where === "start" ? "keep" : bindableElement,
|
return (
|
||||||
elementsMap,
|
selectedElements
|
||||||
),
|
.filter(isLinearElement)
|
||||||
|
.flatMap((element) =>
|
||||||
|
getOriginalBindingsIfStillCloseToArrowEnds(element, app),
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
(element): element is NonDeleted<ExcalidrawBindableElement> =>
|
||||||
|
element !== null,
|
||||||
|
)
|
||||||
|
// Filter out bind candidates which are in the
|
||||||
|
// same selection / group with the arrow
|
||||||
|
//
|
||||||
|
// TODO: Is it worth turning the list into a set to avoid dupes?
|
||||||
|
.filter(
|
||||||
|
(element) =>
|
||||||
|
selectedElements.filter((selected) => selected.id === element?.id)
|
||||||
|
.length === 0,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -283,29 +403,14 @@ export const isLinearElementSimpleAndAlreadyBound = (
|
|||||||
bindableElement: ExcalidrawBindableElement,
|
bindableElement: ExcalidrawBindableElement,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
return (
|
return (
|
||||||
alreadyBoundToId === bindableElement.id && linearElement.points.length < 3
|
alreadyBoundToId === bindableElement.id &&
|
||||||
|
isLinearElementSimple(linearElement)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unbindLinearElements = (
|
const isLinearElementSimple = (
|
||||||
elements: readonly NonDeleted<ExcalidrawElement>[],
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
): boolean => linearElement.points.length < 3;
|
||||||
): void => {
|
|
||||||
elements.forEach((element) => {
|
|
||||||
if (isBindingElement(element)) {
|
|
||||||
if (element.startBinding !== null && element.endBinding !== null) {
|
|
||||||
bindOrUnbindLinearElement(element, null, null, elementsMap);
|
|
||||||
} else {
|
|
||||||
bindOrUnbindLinearElement(
|
|
||||||
element,
|
|
||||||
element.startBinding ? "keep" : null,
|
|
||||||
element.endBinding ? "keep" : null,
|
|
||||||
elementsMap,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unbindLinearElement = (
|
const unbindLinearElement = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
@@ -552,42 +657,6 @@ const maybeCalculateNewGapWhenScaling = (
|
|||||||
return { elementId, gap: newGap, focus };
|
return { elementId, gap: newGap, focus };
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this is a bottleneck, optimise
|
|
||||||
export const getEligibleElementsForBinding = (
|
|
||||||
selectedElements: NonDeleted<ExcalidrawElement>[],
|
|
||||||
app: AppClassProperties,
|
|
||||||
): SuggestedBinding[] => {
|
|
||||||
const includedElementIds = new Set(selectedElements.map(({ id }) => id));
|
|
||||||
return selectedElements.flatMap((selectedElement) =>
|
|
||||||
isBindingElement(selectedElement, false)
|
|
||||||
? (getElligibleElementsForBindingElement(
|
|
||||||
selectedElement as NonDeleted<ExcalidrawLinearElement>,
|
|
||||||
app,
|
|
||||||
).filter(
|
|
||||||
(element) => !includedElementIds.has(element.id),
|
|
||||||
) as SuggestedBinding[])
|
|
||||||
: isBindableElement(selectedElement, false)
|
|
||||||
? getElligibleElementsForBindableElementAndWhere(
|
|
||||||
selectedElement,
|
|
||||||
app,
|
|
||||||
).filter((binding) => !includedElementIds.has(binding[0].id))
|
|
||||||
: [],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getElligibleElementsForBindingElement = (
|
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
|
||||||
app: AppClassProperties,
|
|
||||||
): NonDeleted<ExcalidrawBindableElement>[] => {
|
|
||||||
return [
|
|
||||||
getElligibleElementForBindingElement(linearElement, "start", app),
|
|
||||||
getElligibleElementForBindingElement(linearElement, "end", app),
|
|
||||||
].filter(
|
|
||||||
(element): element is NonDeleted<ExcalidrawBindableElement> =>
|
|
||||||
element != null,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getElligibleElementForBindingElement = (
|
const getElligibleElementForBindingElement = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
@@ -618,67 +687,6 @@ const getLinearElementEdgeCoors = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getElligibleElementsForBindableElementAndWhere = (
|
|
||||||
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
|
||||||
app: AppClassProperties,
|
|
||||||
): SuggestedPointBinding[] => {
|
|
||||||
const scene = Scene.getScene(bindableElement)!;
|
|
||||||
return scene
|
|
||||||
.getNonDeletedElements()
|
|
||||||
.map((element) => {
|
|
||||||
if (!isBindingElement(element, false)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const canBindStart = isLinearElementEligibleForNewBindingByBindable(
|
|
||||||
element,
|
|
||||||
"start",
|
|
||||||
bindableElement,
|
|
||||||
scene.getNonDeletedElementsMap(),
|
|
||||||
app,
|
|
||||||
);
|
|
||||||
const canBindEnd = isLinearElementEligibleForNewBindingByBindable(
|
|
||||||
element,
|
|
||||||
"end",
|
|
||||||
bindableElement,
|
|
||||||
scene.getNonDeletedElementsMap(),
|
|
||||||
app,
|
|
||||||
);
|
|
||||||
if (!canBindStart && !canBindEnd) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
element,
|
|
||||||
canBindStart && canBindEnd ? "both" : canBindStart ? "start" : "end",
|
|
||||||
bindableElement,
|
|
||||||
];
|
|
||||||
})
|
|
||||||
.filter((maybeElement) => maybeElement != null) as SuggestedPointBinding[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const isLinearElementEligibleForNewBindingByBindable = (
|
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
|
||||||
startOrEnd: "start" | "end",
|
|
||||||
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
|
||||||
app: AppClassProperties,
|
|
||||||
): boolean => {
|
|
||||||
const existingBinding =
|
|
||||||
linearElement[startOrEnd === "start" ? "startBinding" : "endBinding"];
|
|
||||||
return (
|
|
||||||
existingBinding == null &&
|
|
||||||
!isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
|
|
||||||
linearElement,
|
|
||||||
bindableElement,
|
|
||||||
startOrEnd,
|
|
||||||
) &&
|
|
||||||
bindingBorderTest(
|
|
||||||
bindableElement,
|
|
||||||
getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap),
|
|
||||||
app,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// We need to:
|
// We need to:
|
||||||
// 1: Update elements not selected to point to duplicated elements
|
// 1: Update elements not selected to point to duplicated elements
|
||||||
// 2: Update duplicated elements to point to other duplicated elements
|
// 2: Update duplicated elements to point to other duplicated elements
|
||||||
@@ -814,7 +822,7 @@ const newBoundElements = (
|
|||||||
return nextBoundElements;
|
return nextBoundElements;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const bindingBorderTest = (
|
const bindingBorderTest = (
|
||||||
element: NonDeleted<ExcalidrawBindableElement>,
|
element: NonDeleted<ExcalidrawBindableElement>,
|
||||||
{ x, y }: { x: number; y: number },
|
{ x, y }: { x: number; y: number },
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
@@ -836,7 +844,7 @@ export const maxBindingGap = (
|
|||||||
return Math.max(16, Math.min(0.25 * smallerDimension, 32));
|
return Math.max(16, Math.min(0.25 * smallerDimension, 32));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const distanceToBindableElement = (
|
const distanceToBindableElement = (
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
point: Point,
|
point: Point,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
@@ -893,7 +901,7 @@ const distanceToDiamond = (
|
|||||||
return GAPoint.distanceToLine(pointRel, side);
|
return GAPoint.distanceToLine(pointRel, side);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const distanceToEllipse = (
|
const distanceToEllipse = (
|
||||||
element: ExcalidrawEllipseElement,
|
element: ExcalidrawEllipseElement,
|
||||||
point: Point,
|
point: Point,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
@@ -1012,7 +1020,7 @@ const coordsCenter = (
|
|||||||
// all focus points lie, so it's a number between -1 and 1.
|
// all focus points lie, so it's a number between -1 and 1.
|
||||||
// The line going through `a` and `b` is a tangent to the "focus image"
|
// The line going through `a` and `b` is a tangent to the "focus image"
|
||||||
// of the element.
|
// of the element.
|
||||||
export const determineFocusDistance = (
|
const determineFocusDistance = (
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
// Point on the line, in absolute coordinates
|
// Point on the line, in absolute coordinates
|
||||||
a: Point,
|
a: Point,
|
||||||
@@ -1053,7 +1061,7 @@ export const determineFocusDistance = (
|
|||||||
return ret || 0;
|
return ret || 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const determineFocusPoint = (
|
const determineFocusPoint = (
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
// The oriented, relative distance from the center of `element` of the
|
// The oriented, relative distance from the center of `element` of the
|
||||||
// returned focusPoint
|
// returned focusPoint
|
||||||
@@ -1093,7 +1101,7 @@ export const determineFocusPoint = (
|
|||||||
|
|
||||||
// Returns 2 or 0 intersection points between line going through `a` and `b`
|
// Returns 2 or 0 intersection points between line going through `a` and `b`
|
||||||
// and the `element`, in ascending order of distance from `a`.
|
// and the `element`, in ascending order of distance from `a`.
|
||||||
export const intersectElementWithLine = (
|
const intersectElementWithLine = (
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
// Point on the line, in absolute coordinates
|
// Point on the line, in absolute coordinates
|
||||||
a: Point,
|
a: Point,
|
||||||
@@ -1260,7 +1268,7 @@ const getEllipseIntersections = (
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCircleIntersections = (
|
const getCircleIntersections = (
|
||||||
center: GA.Point,
|
center: GA.Point,
|
||||||
radius: number,
|
radius: number,
|
||||||
line: GA.Line,
|
line: GA.Line,
|
||||||
@@ -1290,7 +1298,7 @@ export const getCircleIntersections = (
|
|||||||
|
|
||||||
// The focus point is the tangent point of the "focus image" of the
|
// The focus point is the tangent point of the "focus image" of the
|
||||||
// `element`, where the tangent goes through `point`.
|
// `element`, where the tangent goes through `point`.
|
||||||
export const findFocusPointForEllipse = (
|
const findFocusPointForEllipse = (
|
||||||
ellipse: ExcalidrawEllipseElement,
|
ellipse: ExcalidrawEllipseElement,
|
||||||
// Between -1 and 1 (not 0) the relative size of the "focus image" of
|
// Between -1 and 1 (not 0) the relative size of the "focus image" of
|
||||||
// the element on which the focus point lies
|
// the element on which the focus point lies
|
||||||
@@ -1327,7 +1335,7 @@ export const findFocusPointForEllipse = (
|
|||||||
return GA.point(x, (-m * x - 1) / n);
|
return GA.point(x, (-m * x - 1) / n);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findFocusPointForRectangulars = (
|
const findFocusPointForRectangulars = (
|
||||||
element:
|
element:
|
||||||
| ExcalidrawRectangleElement
|
| ExcalidrawRectangleElement
|
||||||
| ExcalidrawImageElement
|
| ExcalidrawImageElement
|
||||||
|
|||||||
@@ -71,13 +71,13 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"penMode": false,
|
"penMode": false,
|
||||||
"pendingImageElementId": null,
|
"pendingImageElementId": null,
|
||||||
"previousSelectedElementIds": {
|
"previousSelectedElementIds": {
|
||||||
"id158": true,
|
"id159": true,
|
||||||
},
|
},
|
||||||
"resizingElement": null,
|
"resizingElement": null,
|
||||||
"scrollX": 0,
|
"scrollX": 0,
|
||||||
"scrollY": 0,
|
"scrollY": 0,
|
||||||
"selectedElementIds": {
|
"selectedElementIds": {
|
||||||
"id158": true,
|
"id159": true,
|
||||||
},
|
},
|
||||||
"selectedElementsAreBeingDragged": false,
|
"selectedElementsAreBeingDragged": false,
|
||||||
"selectedGroupIds": {},
|
"selectedGroupIds": {},
|
||||||
@@ -112,7 +112,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id156",
|
"id": "id157",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -127,7 +127,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 8,
|
"version": 18,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": -100,
|
"x": -100,
|
||||||
"y": -50,
|
"y": -50,
|
||||||
@@ -144,7 +144,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id157",
|
"id": "id158",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -159,7 +159,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 9,
|
"version": 19,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 100,
|
"x": 100,
|
||||||
"y": -50,
|
"y": -50,
|
||||||
@@ -174,7 +174,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"customData": undefined,
|
"customData": undefined,
|
||||||
"endArrowhead": "arrow",
|
"endArrowhead": "arrow",
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id160",
|
"elementId": "id162",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -182,7 +182,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 99.19725525211979,
|
"height": 99.19725525211979,
|
||||||
"id": "id158",
|
"id": "id159",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"lastCommittedPoint": null,
|
"lastCommittedPoint": null,
|
||||||
@@ -210,9 +210,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 24,
|
"version": 54,
|
||||||
"width": 98.40367721010284,
|
"width": 98.40367721010284,
|
||||||
"x": 1.000000000000007,
|
"x": 1,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -223,7 +223,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id158",
|
"id": "id159",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -232,7 +232,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 50,
|
"height": 50,
|
||||||
"id": "id160",
|
"id": "id162",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -274,19 +274,68 @@ History {
|
|||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {},
|
"removed": Map {},
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id156" => Delta {
|
"id159" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [],
|
"endBinding": {
|
||||||
|
"elementId": "id158",
|
||||||
|
"focus": 0.009900990099009901,
|
||||||
|
"gap": 1,
|
||||||
|
},
|
||||||
|
"height": 0.9800031696987099,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
98,
|
||||||
|
-0.9800031696987099,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "id157",
|
||||||
|
"focus": 0.0297029702970297,
|
||||||
|
"gap": 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"boundElements": [
|
"endBinding": {
|
||||||
{
|
"elementId": "id158",
|
||||||
"id": "id158",
|
"focus": -0.02,
|
||||||
"type": "arrow",
|
"gap": 1,
|
||||||
},
|
},
|
||||||
|
"height": 0.0002487679019458344,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
98,
|
||||||
|
0.0002487679019458344,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "id157",
|
||||||
|
"focus": 0.02,
|
||||||
|
"gap": 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HistoryEntry {
|
||||||
|
"appStateChange": AppStateChange {
|
||||||
|
"delta": Delta {
|
||||||
|
"deleted": {},
|
||||||
|
"inserted": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"elementsChange": ElementsChange {
|
||||||
|
"added": Map {},
|
||||||
|
"removed": Map {},
|
||||||
|
"updated": Map {
|
||||||
"id157" => Delta {
|
"id157" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [],
|
"boundElements": [],
|
||||||
@@ -294,16 +343,29 @@ History {
|
|||||||
"inserted": {
|
"inserted": {
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id158",
|
"id": "id159",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id158" => Delta {
|
"id158" => Delta {
|
||||||
|
"deleted": {
|
||||||
|
"boundElements": [],
|
||||||
|
},
|
||||||
|
"inserted": {
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"id": "id159",
|
||||||
|
"type": "arrow",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id159" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id160",
|
"elementId": "id162",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -323,11 +385,11 @@ History {
|
|||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id157",
|
"elementId": "id158",
|
||||||
"focus": 0.009900990099009901,
|
"focus": 0.009900990099009901,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"height": 0.9800000000000002,
|
"height": 0.9802432787444684,
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
@@ -335,22 +397,22 @@ History {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
98,
|
98,
|
||||||
-0.9800000000000002,
|
-0.9802432787444684,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id156",
|
"elementId": "id157",
|
||||||
"focus": 0.0297029702970297,
|
"focus": 0.0297029702970297,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"y": 0.9900000000000004,
|
"y": 0.9903686540602428,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id160" => Delta {
|
"id162" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id158",
|
"id": "id159",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -374,7 +436,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id156" => Delta {
|
"id157" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -405,7 +467,7 @@ History {
|
|||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id157" => Delta {
|
"id158" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -445,9 +507,9 @@ History {
|
|||||||
"delta": Delta {
|
"delta": Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"selectedElementIds": {
|
"selectedElementIds": {
|
||||||
"id158": true,
|
"id159": true,
|
||||||
},
|
},
|
||||||
"selectedLinearElementId": "id158",
|
"selectedLinearElementId": "id159",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"selectedElementIds": {},
|
"selectedElementIds": {},
|
||||||
@@ -458,7 +520,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id158" => Delta {
|
"id159" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -514,7 +576,7 @@ History {
|
|||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of elements 1`] = `4`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of elements 1`] = `4`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of renders 1`] = `15`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of renders 1`] = `22`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] appState 1`] = `
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] appState 1`] = `
|
||||||
{
|
{
|
||||||
@@ -722,7 +784,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 23,
|
"version": 34,
|
||||||
"width": 0,
|
"width": 0,
|
||||||
"x": 251,
|
"x": 251,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
@@ -738,6 +800,46 @@ History {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"redoStack": [
|
"redoStack": [
|
||||||
|
HistoryEntry {
|
||||||
|
"appStateChange": AppStateChange {
|
||||||
|
"delta": Delta {
|
||||||
|
"deleted": {},
|
||||||
|
"inserted": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"elementsChange": ElementsChange {
|
||||||
|
"added": Map {},
|
||||||
|
"removed": Map {},
|
||||||
|
"updated": Map {
|
||||||
|
"id154" => Delta {
|
||||||
|
"deleted": {
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"inserted": {
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
HistoryEntry {
|
HistoryEntry {
|
||||||
"appStateChange": AppStateChange {
|
"appStateChange": AppStateChange {
|
||||||
"delta": Delta {
|
"delta": Delta {
|
||||||
@@ -968,7 +1070,7 @@ History {
|
|||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of elements 1`] = `3`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of elements 1`] = `3`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of renders 1`] = `16`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of renders 1`] = `24`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind remotely added arrow when it's bindable elements are added through the history > [end of test] appState 1`] = `
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind remotely added arrow when it's bindable elements are added through the history > [end of test] appState 1`] = `
|
||||||
{
|
{
|
||||||
@@ -1079,7 +1181,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"customData": undefined,
|
"customData": undefined,
|
||||||
"endArrowhead": null,
|
"endArrowhead": null,
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id162",
|
"elementId": "id164",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1087,7 +1189,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 0.03596020595764898,
|
"height": 0.03596020595764898,
|
||||||
"id": "id163",
|
"id": "id165",
|
||||||
"index": "Zz",
|
"index": "Zz",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"lastCommittedPoint": null,
|
"lastCommittedPoint": null,
|
||||||
@@ -1110,7 +1212,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
},
|
},
|
||||||
"startArrowhead": null,
|
"startArrowhead": null,
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id161",
|
"elementId": "id163",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1132,7 +1234,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id163",
|
"id": "id165",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1141,7 +1243,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id161",
|
"id": "id163",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -1169,7 +1271,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id163",
|
"id": "id165",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1178,7 +1280,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id162",
|
"id": "id164",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -1220,7 +1322,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id161" => Delta {
|
"id163" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -1251,7 +1353,7 @@ History {
|
|||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id162" => Delta {
|
"id164" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -1284,15 +1386,15 @@ History {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id163" => Delta {
|
"id165" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id162",
|
"elementId": "id164",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id161",
|
"elementId": "id163",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1422,7 +1524,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"customData": undefined,
|
"customData": undefined,
|
||||||
"endArrowhead": null,
|
"endArrowhead": null,
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id165",
|
"elementId": "id167",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1430,7 +1532,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 0.03596020595764898,
|
"height": 0.03596020595764898,
|
||||||
"id": "id166",
|
"id": "id168",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"lastCommittedPoint": null,
|
"lastCommittedPoint": null,
|
||||||
@@ -1453,7 +1555,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
},
|
},
|
||||||
"startArrowhead": null,
|
"startArrowhead": null,
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id164",
|
"elementId": "id166",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1475,7 +1577,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id166",
|
"id": "id168",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1484,7 +1586,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id164",
|
"id": "id166",
|
||||||
"index": "a0V",
|
"index": "a0V",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -1512,7 +1614,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id166",
|
"id": "id168",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1521,7 +1623,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id165",
|
"id": "id167",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -1563,7 +1665,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id166" => Delta {
|
"id168" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -1571,7 +1673,7 @@ History {
|
|||||||
"customData": undefined,
|
"customData": undefined,
|
||||||
"endArrowhead": null,
|
"endArrowhead": null,
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id165",
|
"elementId": "id167",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1601,7 +1703,7 @@ History {
|
|||||||
},
|
},
|
||||||
"startArrowhead": null,
|
"startArrowhead": null,
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id164",
|
"elementId": "id166",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -1619,11 +1721,11 @@ History {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id164" => Delta {
|
"id166" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id166",
|
"id": "id168",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1632,11 +1734,11 @@ History {
|
|||||||
"boundElements": [],
|
"boundElements": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id165" => Delta {
|
"id167" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id166",
|
"id": "id168",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1767,7 +1869,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id167",
|
"id": "id169",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -1799,7 +1901,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id168",
|
"id": "id170",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -1841,7 +1943,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id167" => Delta {
|
"id169" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -1872,7 +1974,7 @@ History {
|
|||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id168" => Delta {
|
"id170" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -1990,7 +2092,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"scrollX": 0,
|
"scrollX": 0,
|
||||||
"scrollY": 0,
|
"scrollY": 0,
|
||||||
"selectedElementIds": {
|
"selectedElementIds": {
|
||||||
"id171": true,
|
"id173": true,
|
||||||
},
|
},
|
||||||
"selectedElementsAreBeingDragged": false,
|
"selectedElementsAreBeingDragged": false,
|
||||||
"selectedGroupIds": {},
|
"selectedGroupIds": {},
|
||||||
@@ -2021,7 +2123,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id171",
|
"id": "id173",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -2030,7 +2132,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id169",
|
"id": "id171",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -2058,7 +2160,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id171",
|
"id": "id173",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -2067,7 +2169,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id170",
|
"id": "id172",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -2097,7 +2199,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"customData": undefined,
|
"customData": undefined,
|
||||||
"endArrowhead": "arrow",
|
"endArrowhead": "arrow",
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id170",
|
"elementId": "id172",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -2105,7 +2207,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 373.7994222717614,
|
"height": 373.7994222717614,
|
||||||
"id": "id171",
|
"id": "id173",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"lastCommittedPoint": null,
|
"lastCommittedPoint": null,
|
||||||
@@ -2128,7 +2230,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
},
|
},
|
||||||
"startArrowhead": null,
|
"startArrowhead": null,
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id169",
|
"elementId": "id171",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -2164,7 +2266,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id169" => Delta {
|
"id171" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -2195,7 +2297,7 @@ History {
|
|||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id170" => Delta {
|
"id172" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -2235,9 +2337,9 @@ History {
|
|||||||
"delta": Delta {
|
"delta": Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"selectedElementIds": {
|
"selectedElementIds": {
|
||||||
"id171": true,
|
"id173": true,
|
||||||
},
|
},
|
||||||
"selectedLinearElementId": "id171",
|
"selectedLinearElementId": "id173",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"selectedElementIds": {},
|
"selectedElementIds": {},
|
||||||
@@ -2248,7 +2350,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id171" => Delta {
|
"id173" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -2256,7 +2358,7 @@ History {
|
|||||||
"customData": undefined,
|
"customData": undefined,
|
||||||
"endArrowhead": "arrow",
|
"endArrowhead": "arrow",
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id170",
|
"elementId": "id172",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -2286,7 +2388,7 @@ History {
|
|||||||
},
|
},
|
||||||
"startArrowhead": null,
|
"startArrowhead": null,
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id169",
|
"elementId": "id171",
|
||||||
"focus": 0,
|
"focus": 0,
|
||||||
"gap": 1,
|
"gap": 1,
|
||||||
},
|
},
|
||||||
@@ -2304,11 +2406,11 @@ History {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id169" => Delta {
|
"id171" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id171",
|
"id": "id173",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -2317,11 +2419,11 @@ History {
|
|||||||
"boundElements": [],
|
"boundElements": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"id170" => Delta {
|
"id172" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"boundElements": [
|
"boundElements": [
|
||||||
{
|
{
|
||||||
"id": "id171",
|
"id": "id173",
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -5199,7 +5301,7 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 100,
|
"height": 100,
|
||||||
"id": "id173",
|
"id": "id175",
|
||||||
"index": "Zz",
|
"index": "Zz",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -5231,7 +5333,7 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre
|
|||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": 500,
|
"height": 500,
|
||||||
"id": "id172",
|
"id": "id174",
|
||||||
"index": "a0",
|
"index": "a0",
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -5274,7 +5376,7 @@ History {
|
|||||||
"elementsChange": ElementsChange {
|
"elementsChange": ElementsChange {
|
||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {
|
"removed": Map {
|
||||||
"id173" => Delta {
|
"id175" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"backgroundColor": "transparent",
|
"backgroundColor": "transparent",
|
||||||
@@ -5320,9 +5422,9 @@ History {
|
|||||||
"added": Map {},
|
"added": Map {},
|
||||||
"removed": Map {},
|
"removed": Map {},
|
||||||
"updated": Map {
|
"updated": Map {
|
||||||
"id173" => Delta {
|
"id175" => Delta {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"frameId": "id172",
|
"frameId": "id174",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ describe("element binding", () => {
|
|||||||
const rect = API.createElement({
|
const rect = API.createElement({
|
||||||
type: "rectangle",
|
type: "rectangle",
|
||||||
x: 0,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
||||||
});
|
});
|
||||||
@@ -39,32 +40,43 @@ describe("element binding", () => {
|
|||||||
h.elements = [rect, arrow];
|
h.elements = [rect, arrow];
|
||||||
expect(arrow.startBinding).toBe(null);
|
expect(arrow.startBinding).toBe(null);
|
||||||
|
|
||||||
API.setSelectedElements([arrow]);
|
// select arrow
|
||||||
|
mouse.clickAt(150, 0);
|
||||||
|
|
||||||
expect(API.getSelectedElements()).toEqual([arrow]);
|
// move arrow start to potential binding position
|
||||||
mouse.downAt(100, 0);
|
mouse.downAt(100, 0);
|
||||||
mouse.moveTo(55, 0);
|
mouse.moveTo(55, 0);
|
||||||
mouse.up(0, 0);
|
mouse.up(0, 0);
|
||||||
expect(API.getSelectedElements()).toEqual([arrow]);
|
|
||||||
expect(arrow.startBinding).toEqual({
|
|
||||||
elementId: rect.id,
|
|
||||||
focus: expect.toBeNonNaNNumber(),
|
|
||||||
gap: expect.toBeNonNaNNumber(),
|
|
||||||
});
|
|
||||||
|
|
||||||
mouse.downAt(100, 0);
|
// Point selection is evaluated like the points are rendered,
|
||||||
mouse.move(-45, 0);
|
// from right to left. So clicking on the first point should move the joint,
|
||||||
mouse.up();
|
// not the start point.
|
||||||
expect(arrow.startBinding).toEqual({
|
|
||||||
elementId: rect.id,
|
|
||||||
focus: expect.toBeNonNaNNumber(),
|
|
||||||
gap: expect.toBeNonNaNNumber(),
|
|
||||||
});
|
|
||||||
|
|
||||||
mouse.down();
|
|
||||||
mouse.move(-50, 0);
|
|
||||||
mouse.up();
|
|
||||||
expect(arrow.startBinding).toBe(null);
|
expect(arrow.startBinding).toBe(null);
|
||||||
|
|
||||||
|
// Now that the start point is free, move it into overlapping position
|
||||||
|
mouse.downAt(100, 0);
|
||||||
|
mouse.moveTo(55, 0);
|
||||||
|
mouse.up(0, 0);
|
||||||
|
|
||||||
|
expect(API.getSelectedElements()).toEqual([arrow]);
|
||||||
|
|
||||||
|
expect(arrow.startBinding).toEqual({
|
||||||
|
elementId: rect.id,
|
||||||
|
focus: expect.toBeNonNaNNumber(),
|
||||||
|
gap: expect.toBeNonNaNNumber(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move the end point to the overlapping binding position
|
||||||
|
mouse.downAt(200, 0);
|
||||||
|
mouse.moveTo(55, 0);
|
||||||
|
mouse.up(0, 0);
|
||||||
|
|
||||||
|
// Both the start and the end points should be bound
|
||||||
|
expect(arrow.startBinding).toEqual({
|
||||||
|
elementId: rect.id,
|
||||||
|
focus: expect.toBeNonNaNNumber(),
|
||||||
|
gap: expect.toBeNonNaNNumber(),
|
||||||
|
});
|
||||||
expect(arrow.endBinding).toEqual({
|
expect(arrow.endBinding).toEqual({
|
||||||
elementId: rect.id,
|
elementId: rect.id,
|
||||||
focus: expect.toBeNonNaNNumber(),
|
focus: expect.toBeNonNaNNumber(),
|
||||||
@@ -144,7 +156,7 @@ describe("element binding", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it("should bind/unbind arrow when moving it with keyboard", () => {
|
it("should unbind arrow when moving it with keyboard", () => {
|
||||||
const rectangle = UI.createElement("rectangle", {
|
const rectangle = UI.createElement("rectangle", {
|
||||||
x: 75,
|
x: 75,
|
||||||
y: 0,
|
y: 0,
|
||||||
@@ -160,11 +172,22 @@ describe("element binding", () => {
|
|||||||
|
|
||||||
expect(arrow.endBinding).toBe(null);
|
expect(arrow.endBinding).toBe(null);
|
||||||
|
|
||||||
|
mouse.downAt(50, 50);
|
||||||
|
mouse.moveTo(51, 0);
|
||||||
|
mouse.up(0, 0);
|
||||||
|
|
||||||
|
// Test sticky connection
|
||||||
expect(API.getSelectedElement().type).toBe("arrow");
|
expect(API.getSelectedElement().type).toBe("arrow");
|
||||||
Keyboard.keyPress(KEYS.ARROW_RIGHT);
|
Keyboard.keyPress(KEYS.ARROW_RIGHT);
|
||||||
expect(arrow.endBinding?.elementId).toBe(rectangle.id);
|
expect(arrow.endBinding?.elementId).toBe(rectangle.id);
|
||||||
|
|
||||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||||
|
expect(arrow.endBinding?.elementId).toBe(rectangle.id);
|
||||||
|
|
||||||
|
// Sever connection
|
||||||
|
expect(API.getSelectedElement().type).toBe("arrow");
|
||||||
|
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||||
|
expect(arrow.endBinding).toBe(null);
|
||||||
|
Keyboard.keyPress(KEYS.ARROW_RIGHT);
|
||||||
expect(arrow.endBinding).toBe(null);
|
expect(arrow.endBinding).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4014,12 +4014,18 @@ describe("history", () => {
|
|||||||
|
|
||||||
const arrowId = h.elements[2].id;
|
const arrowId = h.elements[2].id;
|
||||||
|
|
||||||
// create binding
|
// create start binding
|
||||||
mouse.downAt(0, 0);
|
mouse.downAt(0, 0);
|
||||||
mouse.moveTo(0, 1);
|
mouse.moveTo(0, 1);
|
||||||
mouse.moveTo(0, 0);
|
mouse.moveTo(0, 0);
|
||||||
mouse.up();
|
mouse.up();
|
||||||
|
|
||||||
|
// create end binding
|
||||||
|
mouse.downAt(100, 0);
|
||||||
|
mouse.moveTo(100, 1);
|
||||||
|
mouse.moveTo(100, 0);
|
||||||
|
mouse.up();
|
||||||
|
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: rect1.id,
|
id: rect1.id,
|
||||||
@@ -4044,9 +4050,10 @@ describe("history", () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Keyboard.undo();
|
Keyboard.undo(); // undo start binding
|
||||||
|
Keyboard.undo(); // undo end binding
|
||||||
expect(API.getUndoStack().length).toBe(2);
|
expect(API.getUndoStack().length).toBe(2);
|
||||||
expect(API.getRedoStack().length).toBe(1);
|
expect(API.getRedoStack().length).toBe(2);
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: rect1.id,
|
id: rect1.id,
|
||||||
@@ -4081,7 +4088,8 @@ describe("history", () => {
|
|||||||
|
|
||||||
runTwice(() => {
|
runTwice(() => {
|
||||||
Keyboard.redo();
|
Keyboard.redo();
|
||||||
expect(API.getUndoStack().length).toBe(3);
|
Keyboard.redo();
|
||||||
|
expect(API.getUndoStack().length).toBe(4);
|
||||||
expect(API.getRedoStack().length).toBe(0);
|
expect(API.getRedoStack().length).toBe(0);
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -4107,9 +4115,10 @@ describe("history", () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Keyboard.undo();
|
||||||
Keyboard.undo();
|
Keyboard.undo();
|
||||||
expect(API.getUndoStack().length).toBe(2);
|
expect(API.getUndoStack().length).toBe(2);
|
||||||
expect(API.getRedoStack().length).toBe(1);
|
expect(API.getRedoStack().length).toBe(2);
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: rect1.id,
|
id: rect1.id,
|
||||||
@@ -4135,11 +4144,16 @@ describe("history", () => {
|
|||||||
|
|
||||||
const arrowId = h.elements[2].id;
|
const arrowId = h.elements[2].id;
|
||||||
|
|
||||||
// create binding
|
// create start binding
|
||||||
mouse.downAt(0, 0);
|
mouse.downAt(0, 0);
|
||||||
mouse.moveTo(0, 1);
|
mouse.moveTo(0, 1);
|
||||||
mouse.upAt(0, 0);
|
mouse.upAt(0, 0);
|
||||||
|
|
||||||
|
// create end binding
|
||||||
|
mouse.downAt(100, 0);
|
||||||
|
mouse.moveTo(100, 1);
|
||||||
|
mouse.upAt(100, 0);
|
||||||
|
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: rect1.id,
|
id: rect1.id,
|
||||||
@@ -4164,9 +4178,10 @@ describe("history", () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Keyboard.undo();
|
||||||
Keyboard.undo();
|
Keyboard.undo();
|
||||||
expect(API.getUndoStack().length).toBe(2);
|
expect(API.getUndoStack().length).toBe(2);
|
||||||
expect(API.getRedoStack().length).toBe(1);
|
expect(API.getRedoStack().length).toBe(2);
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: rect1.id,
|
id: rect1.id,
|
||||||
@@ -4202,7 +4217,8 @@ describe("history", () => {
|
|||||||
|
|
||||||
runTwice(() => {
|
runTwice(() => {
|
||||||
Keyboard.redo();
|
Keyboard.redo();
|
||||||
expect(API.getUndoStack().length).toBe(3);
|
Keyboard.redo();
|
||||||
|
expect(API.getUndoStack().length).toBe(4);
|
||||||
expect(API.getRedoStack().length).toBe(0);
|
expect(API.getRedoStack().length).toBe(0);
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -4233,9 +4249,10 @@ describe("history", () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Keyboard.undo();
|
||||||
Keyboard.undo();
|
Keyboard.undo();
|
||||||
expect(API.getUndoStack().length).toBe(2);
|
expect(API.getUndoStack().length).toBe(2);
|
||||||
expect(API.getRedoStack().length).toBe(1);
|
expect(API.getRedoStack().length).toBe(2);
|
||||||
expect(h.elements).toEqual([
|
expect(h.elements).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: rect1.id,
|
id: rect1.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user