|
@@ -20,7 +20,6 @@ import {
|
|
|
getElementMap,
|
|
|
getDrawingVersion,
|
|
|
getSyncableElements,
|
|
|
- hasNonDeletedElements,
|
|
|
newLinearElement,
|
|
|
ResizeArrowFnType,
|
|
|
resizeElements,
|
|
@@ -185,7 +184,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
this.actionManager = new ActionManager(
|
|
|
this.syncActionResult,
|
|
|
() => this.state,
|
|
|
- () => globalSceneState.getAllElements(),
|
|
|
+ () => globalSceneState.getElementsIncludingDeleted(),
|
|
|
);
|
|
|
this.actionManager.registerAll(actions);
|
|
|
|
|
@@ -209,10 +208,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
appState={this.state}
|
|
|
setAppState={this.setAppState}
|
|
|
actionManager={this.actionManager}
|
|
|
- elements={globalSceneState.getAllElements().filter((element) => {
|
|
|
- return !element.isDeleted;
|
|
|
- })}
|
|
|
- setElements={this.setElements}
|
|
|
+ elements={globalSceneState.getElements()}
|
|
|
onRoomCreate={this.openPortal}
|
|
|
onRoomDestroy={this.closePortal}
|
|
|
onLockToggle={this.toggleLock}
|
|
@@ -310,7 +306,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
try {
|
|
|
await Promise.race([
|
|
|
document.fonts?.ready?.then(() => {
|
|
|
- globalSceneState.getAllElements().forEach((element) => {
|
|
|
+ globalSceneState.getElementsIncludingDeleted().forEach((element) => {
|
|
|
if (isTextElement(element)) {
|
|
|
invalidateShapeForElement(element);
|
|
|
}
|
|
@@ -431,7 +427,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
}
|
|
|
private onResize = withBatchedUpdates(() => {
|
|
|
globalSceneState
|
|
|
- .getAllElements()
|
|
|
+ .getElementsIncludingDeleted()
|
|
|
.forEach((element) => invalidateShapeForElement(element));
|
|
|
this.setState({});
|
|
|
});
|
|
@@ -439,7 +435,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
private beforeUnload = withBatchedUpdates((event: BeforeUnloadEvent) => {
|
|
|
if (
|
|
|
this.state.isCollaborating &&
|
|
|
- hasNonDeletedElements(globalSceneState.getAllElements())
|
|
|
+ globalSceneState.getElements().length > 0
|
|
|
) {
|
|
|
event.preventDefault();
|
|
|
// NOTE: modern browsers no longer allow showing a custom message here
|
|
@@ -484,8 +480,9 @@ export class App extends React.Component<any, AppState> {
|
|
|
);
|
|
|
cursorButton[socketID] = user.button;
|
|
|
});
|
|
|
+ const elements = globalSceneState.getElements();
|
|
|
const { atLeastOneVisibleElement, scrollBars } = renderScene(
|
|
|
- globalSceneState.getAllElements().filter((element) => {
|
|
|
+ elements.filter((element) => {
|
|
|
// don't render text element that's being currently edited (it's
|
|
|
// rendered on remote only)
|
|
|
return (
|
|
@@ -517,22 +514,20 @@ export class App extends React.Component<any, AppState> {
|
|
|
if (scrollBars) {
|
|
|
currentScrollBars = scrollBars;
|
|
|
}
|
|
|
- const scrolledOutside =
|
|
|
- !atLeastOneVisibleElement &&
|
|
|
- hasNonDeletedElements(globalSceneState.getAllElements());
|
|
|
+ const scrolledOutside = !atLeastOneVisibleElement && elements.length > 0;
|
|
|
if (this.state.scrolledOutside !== scrolledOutside) {
|
|
|
this.setState({ scrolledOutside: scrolledOutside });
|
|
|
}
|
|
|
this.saveDebounced();
|
|
|
|
|
|
if (
|
|
|
- getDrawingVersion(globalSceneState.getAllElements()) >
|
|
|
+ getDrawingVersion(globalSceneState.getElementsIncludingDeleted()) >
|
|
|
this.lastBroadcastedOrReceivedSceneVersion
|
|
|
) {
|
|
|
this.broadcastScene("SCENE_UPDATE");
|
|
|
}
|
|
|
|
|
|
- history.record(this.state, globalSceneState.getAllElements());
|
|
|
+ history.record(this.state, globalSceneState.getElementsIncludingDeleted());
|
|
|
}
|
|
|
|
|
|
// Copy/paste
|
|
@@ -543,7 +538,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
}
|
|
|
this.copyAll();
|
|
|
const { elements: nextElements, appState } = deleteSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElementsIncludingDeleted(),
|
|
|
this.state,
|
|
|
);
|
|
|
globalSceneState.replaceAllElements(nextElements);
|
|
@@ -561,19 +556,16 @@ export class App extends React.Component<any, AppState> {
|
|
|
});
|
|
|
|
|
|
private copyAll = () => {
|
|
|
- copyToAppClipboard(globalSceneState.getAllElements(), this.state);
|
|
|
+ copyToAppClipboard(globalSceneState.getElements(), this.state);
|
|
|
};
|
|
|
|
|
|
private copyToClipboardAsPng = () => {
|
|
|
- const selectedElements = getSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
- this.state,
|
|
|
- );
|
|
|
+ const elements = globalSceneState.getElements();
|
|
|
+
|
|
|
+ const selectedElements = getSelectedElements(elements, this.state);
|
|
|
exportCanvas(
|
|
|
"clipboard",
|
|
|
- selectedElements.length
|
|
|
- ? selectedElements
|
|
|
- : globalSceneState.getAllElements(),
|
|
|
+ selectedElements.length ? selectedElements : elements,
|
|
|
this.state,
|
|
|
this.canvas!,
|
|
|
this.state,
|
|
@@ -582,14 +574,14 @@ export class App extends React.Component<any, AppState> {
|
|
|
|
|
|
private copyToClipboardAsSvg = () => {
|
|
|
const selectedElements = getSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElements(),
|
|
|
this.state,
|
|
|
);
|
|
|
exportCanvas(
|
|
|
"clipboard-svg",
|
|
|
selectedElements.length
|
|
|
? selectedElements
|
|
|
- : globalSceneState.getAllElements(),
|
|
|
+ : globalSceneState.getElements(),
|
|
|
this.state,
|
|
|
this.canvas!,
|
|
|
this.state,
|
|
@@ -669,7 +661,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
);
|
|
|
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements(),
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted(),
|
|
|
...newElements,
|
|
|
]);
|
|
|
history.resumeRecording();
|
|
@@ -703,7 +695,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
});
|
|
|
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements(),
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted(),
|
|
|
element,
|
|
|
]);
|
|
|
this.setState({ selectedElementIds: { [element.id]: true } });
|
|
@@ -789,15 +781,15 @@ export class App extends React.Component<any, AppState> {
|
|
|
// elements with more staler versions than ours, ignore them
|
|
|
// and keep ours.
|
|
|
if (
|
|
|
- globalSceneState.getAllElements() == null ||
|
|
|
- globalSceneState.getAllElements().length === 0
|
|
|
+ globalSceneState.getElementsIncludingDeleted() == null ||
|
|
|
+ globalSceneState.getElementsIncludingDeleted().length === 0
|
|
|
) {
|
|
|
globalSceneState.replaceAllElements(remoteElements);
|
|
|
} else {
|
|
|
// create a map of ids so we don't have to iterate
|
|
|
// over the array more than once.
|
|
|
const localElementMap = getElementMap(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElementsIncludingDeleted(),
|
|
|
);
|
|
|
|
|
|
// Reconcile
|
|
@@ -982,12 +974,14 @@ export class App extends React.Component<any, AppState> {
|
|
|
const data: SocketUpdateDataSource[typeof sceneType] = {
|
|
|
type: sceneType,
|
|
|
payload: {
|
|
|
- elements: getSyncableElements(globalSceneState.getAllElements()),
|
|
|
+ elements: getSyncableElements(
|
|
|
+ globalSceneState.getElementsIncludingDeleted(),
|
|
|
+ ),
|
|
|
},
|
|
|
};
|
|
|
this.lastBroadcastedOrReceivedSceneVersion = Math.max(
|
|
|
this.lastBroadcastedOrReceivedSceneVersion,
|
|
|
- getDrawingVersion(globalSceneState.getAllElements()),
|
|
|
+ getDrawingVersion(globalSceneState.getElementsIncludingDeleted()),
|
|
|
);
|
|
|
return this._broadcastSocketData(
|
|
|
data as typeof data & { _brand: "socketUpdateData" },
|
|
@@ -1063,7 +1057,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
? ELEMENT_SHIFT_TRANSLATE_AMOUNT
|
|
|
: ELEMENT_TRANSLATE_AMOUNT;
|
|
|
globalSceneState.replaceAllElements(
|
|
|
- globalSceneState.getAllElements().map((el) => {
|
|
|
+ globalSceneState.getElementsIncludingDeleted().map((el) => {
|
|
|
if (this.state.selectedElementIds[el.id]) {
|
|
|
const update: { x?: number; y?: number } = {};
|
|
|
if (event.key === KEYS.ARROW_LEFT) {
|
|
@@ -1083,7 +1077,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
event.preventDefault();
|
|
|
} else if (event.key === KEYS.ENTER) {
|
|
|
const selectedElements = getSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElements(),
|
|
|
this.state,
|
|
|
);
|
|
|
|
|
@@ -1188,7 +1182,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
|
|
|
const deleteElement = () => {
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements().map((_element) => {
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted().map((_element) => {
|
|
|
if (_element.id === element.id) {
|
|
|
return newElementWith(_element, { isDeleted: true });
|
|
|
}
|
|
@@ -1199,7 +1193,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
|
|
|
const updateElement = (text: string) => {
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements().map((_element) => {
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted().map((_element) => {
|
|
|
if (_element.id === element.id) {
|
|
|
return newTextElement({
|
|
|
...(_element as ExcalidrawTextElement),
|
|
@@ -1271,7 +1265,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
centerIfPossible?: boolean;
|
|
|
}) => {
|
|
|
const elementAtPosition = getElementAtPosition(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElements(),
|
|
|
this.state,
|
|
|
x,
|
|
|
y,
|
|
@@ -1326,7 +1320,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
});
|
|
|
} else {
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements(),
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted(),
|
|
|
element,
|
|
|
]);
|
|
|
|
|
@@ -1503,13 +1497,12 @@ export class App extends React.Component<any, AppState> {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const selectedElements = getSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
- this.state,
|
|
|
- );
|
|
|
+ const elements = globalSceneState.getElements();
|
|
|
+
|
|
|
+ const selectedElements = getSelectedElements(elements, this.state);
|
|
|
if (selectedElements.length === 1 && !isOverScrollBar) {
|
|
|
const elementWithResizeHandler = getElementWithResizeHandler(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ elements,
|
|
|
this.state,
|
|
|
{ x, y },
|
|
|
this.state.zoom,
|
|
@@ -1538,7 +1531,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
}
|
|
|
}
|
|
|
const hitElement = getElementAtPosition(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ elements,
|
|
|
this.state,
|
|
|
x,
|
|
|
y,
|
|
@@ -1737,13 +1730,11 @@ export class App extends React.Component<any, AppState> {
|
|
|
let hitElement: ExcalidrawElement | null = null;
|
|
|
let hitElementWasAddedToSelection = false;
|
|
|
if (this.state.elementType === "selection") {
|
|
|
- const selectedElements = getSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
- this.state,
|
|
|
- );
|
|
|
+ const elements = globalSceneState.getElements();
|
|
|
+ const selectedElements = getSelectedElements(elements, this.state);
|
|
|
if (selectedElements.length === 1) {
|
|
|
const elementWithResizeHandler = getElementWithResizeHandler(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ elements,
|
|
|
this.state,
|
|
|
{ x, y },
|
|
|
this.state.zoom,
|
|
@@ -1781,7 +1772,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
}
|
|
|
if (!isResizingElements) {
|
|
|
hitElement = getElementAtPosition(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ elements,
|
|
|
this.state,
|
|
|
x,
|
|
|
y,
|
|
@@ -1809,7 +1800,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
},
|
|
|
}));
|
|
|
globalSceneState.replaceAllElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElementsIncludingDeleted(),
|
|
|
);
|
|
|
hitElementWasAddedToSelection = true;
|
|
|
}
|
|
@@ -1820,7 +1811,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
// put the duplicates where the selected elements used to be.
|
|
|
const nextElements = [];
|
|
|
const elementsToAppend = [];
|
|
|
- for (const element of globalSceneState.getAllElements()) {
|
|
|
+ for (const element of globalSceneState.getElementsIncludingDeleted()) {
|
|
|
if (
|
|
|
this.state.selectedElementIds[element.id] ||
|
|
|
(element.id === hitElement.id && hitElementWasAddedToSelection)
|
|
@@ -1930,7 +1921,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
points: [...element.points, [0, 0]],
|
|
|
});
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements(),
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted(),
|
|
|
element,
|
|
|
]);
|
|
|
this.setState({
|
|
@@ -1958,7 +1949,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
});
|
|
|
} else {
|
|
|
globalSceneState.replaceAllElements([
|
|
|
- ...globalSceneState.getAllElements(),
|
|
|
+ ...globalSceneState.getElementsIncludingDeleted(),
|
|
|
element,
|
|
|
]);
|
|
|
this.setState({
|
|
@@ -2047,7 +2038,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
// if elements should be deselected on pointerup
|
|
|
draggingOccurred = true;
|
|
|
const selectedElements = getSelectedElements(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElements(),
|
|
|
this.state,
|
|
|
);
|
|
|
if (selectedElements.length > 0) {
|
|
@@ -2123,14 +2114,12 @@ export class App extends React.Component<any, AppState> {
|
|
|
}
|
|
|
|
|
|
if (this.state.elementType === "selection") {
|
|
|
- if (
|
|
|
- !event.shiftKey &&
|
|
|
- isSomeElementSelected(globalSceneState.getAllElements(), this.state)
|
|
|
- ) {
|
|
|
+ const elements = globalSceneState.getElements();
|
|
|
+ if (!event.shiftKey && isSomeElementSelected(elements, this.state)) {
|
|
|
this.setState({ selectedElementIds: {} });
|
|
|
}
|
|
|
const elementsWithinSelection = getElementsWithinSelection(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ elements,
|
|
|
draggingElement,
|
|
|
);
|
|
|
this.setState((prevState) => ({
|
|
@@ -2223,7 +2212,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
) {
|
|
|
// remove invisible element which was added in onPointerDown
|
|
|
globalSceneState.replaceAllElements(
|
|
|
- globalSceneState.getAllElements().slice(0, -1),
|
|
|
+ globalSceneState.getElementsIncludingDeleted().slice(0, -1),
|
|
|
);
|
|
|
this.setState({
|
|
|
draggingElement: null,
|
|
@@ -2240,7 +2229,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
if (resizingElement && isInvisiblySmallElement(resizingElement)) {
|
|
|
globalSceneState.replaceAllElements(
|
|
|
globalSceneState
|
|
|
- .getAllElements()
|
|
|
+ .getElementsIncludingDeleted()
|
|
|
.filter((el) => el.id !== resizingElement.id),
|
|
|
);
|
|
|
}
|
|
@@ -2285,7 +2274,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
|
|
|
if (
|
|
|
elementType !== "selection" ||
|
|
|
- isSomeElementSelected(globalSceneState.getAllElements(), this.state)
|
|
|
+ isSomeElementSelected(globalSceneState.getElements(), this.state)
|
|
|
) {
|
|
|
history.resumeRecording();
|
|
|
}
|
|
@@ -2366,8 +2355,9 @@ export class App extends React.Component<any, AppState> {
|
|
|
window.devicePixelRatio,
|
|
|
);
|
|
|
|
|
|
+ const elements = globalSceneState.getElements();
|
|
|
const element = getElementAtPosition(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ elements,
|
|
|
this.state,
|
|
|
x,
|
|
|
y,
|
|
@@ -2381,12 +2371,12 @@ export class App extends React.Component<any, AppState> {
|
|
|
action: () => this.pasteFromClipboard(null),
|
|
|
},
|
|
|
probablySupportsClipboardBlob &&
|
|
|
- hasNonDeletedElements(globalSceneState.getAllElements()) && {
|
|
|
+ elements.length > 0 && {
|
|
|
label: t("labels.copyAsPng"),
|
|
|
action: this.copyToClipboardAsPng,
|
|
|
},
|
|
|
probablySupportsClipboardWriteText &&
|
|
|
- hasNonDeletedElements(globalSceneState.getAllElements()) && {
|
|
|
+ elements.length > 0 && {
|
|
|
label: t("labels.copyAsSvg"),
|
|
|
action: this.copyToClipboardAsSvg,
|
|
|
},
|
|
@@ -2468,7 +2458,7 @@ export class App extends React.Component<any, AppState> {
|
|
|
scale: number,
|
|
|
) {
|
|
|
const elementClickedInside = getElementContainingPosition(
|
|
|
- globalSceneState.getAllElements(),
|
|
|
+ globalSceneState.getElementsIncludingDeleted(),
|
|
|
x,
|
|
|
y,
|
|
|
);
|
|
@@ -2522,7 +2512,10 @@ export class App extends React.Component<any, AppState> {
|
|
|
}, 300);
|
|
|
|
|
|
private saveDebounced = debounce(() => {
|
|
|
- saveToLocalStorage(globalSceneState.getAllElements(), this.state);
|
|
|
+ saveToLocalStorage(
|
|
|
+ globalSceneState.getElementsIncludingDeleted(),
|
|
|
+ this.state,
|
|
|
+ );
|
|
|
}, 300);
|
|
|
}
|
|
|
|
|
@@ -2548,7 +2541,7 @@ if (process.env.NODE_ENV === "test" || process.env.NODE_ENV === "development") {
|
|
|
Object.defineProperties(window.h, {
|
|
|
elements: {
|
|
|
get() {
|
|
|
- return globalSceneState.getAllElements();
|
|
|
+ return globalSceneState.getElementsIncludingDeleted();
|
|
|
},
|
|
|
set(elements: ExcalidrawElement[]) {
|
|
|
return globalSceneState.replaceAllElements(elements);
|