mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
refactor: decoupling global Scene state part-1 (#7577)
This commit is contained in:
parent
740a165452
commit
0415c616b1
31 changed files with 630 additions and 384 deletions
|
@ -1,7 +1,6 @@
|
|||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { ActionManager } from "../actions/manager";
|
||||
import { getNonDeletedElements } from "../element";
|
||||
import { ExcalidrawElement, ExcalidrawElementType } from "../element/types";
|
||||
import { ExcalidrawElementType, NonDeletedElementsMap } from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
import { useDevice } from "./App";
|
||||
import {
|
||||
|
@ -44,17 +43,14 @@ import { useTunnels } from "../context/tunnels";
|
|||
|
||||
export const SelectedShapeActions = ({
|
||||
appState,
|
||||
elements,
|
||||
elementsMap,
|
||||
renderAction,
|
||||
}: {
|
||||
appState: UIAppState;
|
||||
elements: readonly ExcalidrawElement[];
|
||||
elementsMap: NonDeletedElementsMap;
|
||||
renderAction: ActionManager["renderAction"];
|
||||
}) => {
|
||||
const targetElements = getTargetElements(
|
||||
getNonDeletedElements(elements),
|
||||
appState,
|
||||
);
|
||||
const targetElements = getTargetElements(elementsMap, appState);
|
||||
|
||||
let isSingleElementBoundContainer = false;
|
||||
if (
|
||||
|
@ -137,12 +133,12 @@ export const SelectedShapeActions = ({
|
|||
{renderAction("changeFontFamily")}
|
||||
|
||||
{(appState.activeTool.type === "text" ||
|
||||
suppportsHorizontalAlign(targetElements)) &&
|
||||
suppportsHorizontalAlign(targetElements, elementsMap)) &&
|
||||
renderAction("changeTextAlign")}
|
||||
</>
|
||||
)}
|
||||
|
||||
{shouldAllowVerticalAlign(targetElements) &&
|
||||
{shouldAllowVerticalAlign(targetElements, elementsMap) &&
|
||||
renderAction("changeVerticalAlign")}
|
||||
{(canHaveArrowheads(appState.activeTool.type) ||
|
||||
targetElements.some((element) => canHaveArrowheads(element.type))) && (
|
||||
|
|
|
@ -1417,7 +1417,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
const { renderTopRightUI, renderCustomStats } = this.props;
|
||||
|
||||
const versionNonce = this.scene.getVersionNonce();
|
||||
const { canvasElements, visibleElements } =
|
||||
const { elementsMap, visibleElements } =
|
||||
this.renderer.getRenderableElements({
|
||||
versionNonce,
|
||||
zoom: this.state.zoom,
|
||||
|
@ -1627,7 +1627,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
<StaticCanvas
|
||||
canvas={this.canvas}
|
||||
rc={this.rc}
|
||||
elements={canvasElements}
|
||||
elementsMap={elementsMap}
|
||||
visibleElements={visibleElements}
|
||||
versionNonce={versionNonce}
|
||||
selectionNonce={
|
||||
|
@ -1648,7 +1648,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
<InteractiveCanvas
|
||||
containerRef={this.excalidrawContainerRef}
|
||||
canvas={this.interactiveCanvas}
|
||||
elements={canvasElements}
|
||||
elementsMap={elementsMap}
|
||||
visibleElements={visibleElements}
|
||||
selectedElements={selectedElements}
|
||||
versionNonce={versionNonce}
|
||||
|
@ -2780,7 +2780,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
private renderInteractiveSceneCallback = ({
|
||||
atLeastOneVisibleElement,
|
||||
scrollBars,
|
||||
elements,
|
||||
elementsMap,
|
||||
}: RenderInteractiveSceneCallback) => {
|
||||
if (scrollBars) {
|
||||
currentScrollBars = scrollBars;
|
||||
|
@ -2789,7 +2789,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
// hide when editing text
|
||||
isTextElement(this.state.editingElement)
|
||||
? false
|
||||
: !atLeastOneVisibleElement && elements.length > 0;
|
||||
: !atLeastOneVisibleElement && elementsMap.size > 0;
|
||||
if (this.state.scrolledOutside !== scrolledOutside) {
|
||||
this.setState({ scrolledOutside });
|
||||
}
|
||||
|
@ -3119,7 +3119,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
newElements.forEach((newElement) => {
|
||||
if (isTextElement(newElement) && isBoundToContainer(newElement)) {
|
||||
const container = getContainerElement(newElement);
|
||||
const container = getContainerElement(
|
||||
newElement,
|
||||
this.scene.getElementsMapIncludingDeleted(),
|
||||
);
|
||||
redrawTextBoundingBox(newElement, container);
|
||||
}
|
||||
});
|
||||
|
@ -4183,11 +4186,18 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.scene.replaceAllElements([
|
||||
...this.scene.getElementsIncludingDeleted().map((_element) => {
|
||||
if (_element.id === element.id && isTextElement(_element)) {
|
||||
return updateTextElement(_element, {
|
||||
text,
|
||||
isDeleted,
|
||||
originalText,
|
||||
});
|
||||
return updateTextElement(
|
||||
_element,
|
||||
getContainerElement(
|
||||
_element,
|
||||
this.scene.getElementsMapIncludingDeleted(),
|
||||
),
|
||||
{
|
||||
text,
|
||||
isDeleted,
|
||||
originalText,
|
||||
},
|
||||
);
|
||||
}
|
||||
return _element;
|
||||
}),
|
||||
|
@ -7700,13 +7710,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
groupIds: [],
|
||||
});
|
||||
|
||||
this.scene.replaceAllElements(
|
||||
removeElementsFromFrame(
|
||||
this.scene.getElementsIncludingDeleted(),
|
||||
[linearElement],
|
||||
this.state,
|
||||
),
|
||||
);
|
||||
removeElementsFromFrame([linearElement]);
|
||||
|
||||
this.scene.informMutation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7716,7 +7722,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.getTopLayerFrameAtSceneCoords(sceneCoords);
|
||||
|
||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
||||
let nextElements = this.scene.getElementsIncludingDeleted();
|
||||
let nextElements = this.scene.getElementsMapIncludingDeleted();
|
||||
|
||||
const updateGroupIdsAfterEditingGroup = (
|
||||
elements: ExcalidrawElement[],
|
||||
|
@ -7809,7 +7815,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
this.scene.replaceAllElements(
|
||||
addElementsToFrame(
|
||||
this.scene.getElementsIncludingDeleted(),
|
||||
this.scene.getElementsMapIncludingDeleted(),
|
||||
elementsInsideFrame,
|
||||
draggingElement,
|
||||
),
|
||||
|
@ -7857,7 +7863,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.state,
|
||||
),
|
||||
frame,
|
||||
this.state,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -9137,10 +9142,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
if (
|
||||
transformElements(
|
||||
pointerDownState,
|
||||
pointerDownState.originalElements,
|
||||
transformHandleType,
|
||||
selectedElements,
|
||||
pointerDownState.resize.arrowDirection,
|
||||
this.scene.getElementsMapIncludingDeleted(),
|
||||
shouldRotateWithDiscreteAngle(event),
|
||||
shouldResizeFromCenter(event),
|
||||
selectedElements.length === 1 && isImageElement(selectedElements[0])
|
||||
|
@ -9150,7 +9155,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
resizeY,
|
||||
pointerDownState.resize.center.x,
|
||||
pointerDownState.resize.center.y,
|
||||
this.state,
|
||||
)
|
||||
) {
|
||||
this.maybeSuggestBindingForAll(selectedElements);
|
||||
|
|
|
@ -226,7 +226,7 @@ const LayerUI = ({
|
|||
>
|
||||
<SelectedShapeActions
|
||||
appState={appState}
|
||||
elements={elements}
|
||||
elementsMap={app.scene.getNonDeletedElementsMap()}
|
||||
renderAction={actionManager.renderAction}
|
||||
/>
|
||||
</Island>
|
||||
|
|
|
@ -183,7 +183,7 @@ export const MobileMenu = ({
|
|||
<Section className="App-mobile-menu" heading="selectedShapeActions">
|
||||
<SelectedShapeActions
|
||||
appState={appState}
|
||||
elements={elements}
|
||||
elementsMap={app.scene.getNonDeletedElementsMap()}
|
||||
renderAction={actionManager.renderAction}
|
||||
/>
|
||||
</Section>
|
||||
|
|
|
@ -7,6 +7,7 @@ import type { DOMAttributes } from "react";
|
|||
import type { AppState, InteractiveCanvasAppState } from "../../types";
|
||||
import type {
|
||||
InteractiveCanvasRenderConfig,
|
||||
RenderableElementsMap,
|
||||
RenderInteractiveSceneCallback,
|
||||
} from "../../scene/types";
|
||||
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
||||
|
@ -15,7 +16,7 @@ import { isRenderThrottlingEnabled } from "../../reactUtils";
|
|||
type InteractiveCanvasProps = {
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
elementsMap: RenderableElementsMap;
|
||||
visibleElements: readonly NonDeletedExcalidrawElement[];
|
||||
selectedElements: readonly NonDeletedExcalidrawElement[];
|
||||
versionNonce: number | undefined;
|
||||
|
@ -113,7 +114,7 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
|
|||
renderInteractiveScene(
|
||||
{
|
||||
canvas: props.canvas,
|
||||
elements: props.elements,
|
||||
elementsMap: props.elementsMap,
|
||||
visibleElements: props.visibleElements,
|
||||
selectedElements: props.selectedElements,
|
||||
scale: window.devicePixelRatio,
|
||||
|
@ -201,10 +202,10 @@ const areEqual = (
|
|||
prevProps.selectionNonce !== nextProps.selectionNonce ||
|
||||
prevProps.versionNonce !== nextProps.versionNonce ||
|
||||
prevProps.scale !== nextProps.scale ||
|
||||
// we need to memoize on element arrays because they may have renewed
|
||||
// we need to memoize on elementsMap because they may have renewed
|
||||
// even if versionNonce didn't change (e.g. we filter elements out based
|
||||
// on appState)
|
||||
prevProps.elements !== nextProps.elements ||
|
||||
prevProps.elementsMap !== nextProps.elementsMap ||
|
||||
prevProps.visibleElements !== nextProps.visibleElements ||
|
||||
prevProps.selectedElements !== nextProps.selectedElements
|
||||
) {
|
||||
|
|
|
@ -3,14 +3,17 @@ import { RoughCanvas } from "roughjs/bin/canvas";
|
|||
import { renderStaticScene } from "../../renderer/renderScene";
|
||||
import { isShallowEqual } from "../../utils";
|
||||
import type { AppState, StaticCanvasAppState } from "../../types";
|
||||
import type { StaticCanvasRenderConfig } from "../../scene/types";
|
||||
import type {
|
||||
RenderableElementsMap,
|
||||
StaticCanvasRenderConfig,
|
||||
} from "../../scene/types";
|
||||
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
||||
import { isRenderThrottlingEnabled } from "../../reactUtils";
|
||||
|
||||
type StaticCanvasProps = {
|
||||
canvas: HTMLCanvasElement;
|
||||
rc: RoughCanvas;
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
elementsMap: RenderableElementsMap;
|
||||
visibleElements: readonly NonDeletedExcalidrawElement[];
|
||||
versionNonce: number | undefined;
|
||||
selectionNonce: number | undefined;
|
||||
|
@ -63,7 +66,7 @@ const StaticCanvas = (props: StaticCanvasProps) => {
|
|||
canvas,
|
||||
rc: props.rc,
|
||||
scale: props.scale,
|
||||
elements: props.elements,
|
||||
elementsMap: props.elementsMap,
|
||||
visibleElements: props.visibleElements,
|
||||
appState: props.appState,
|
||||
renderConfig: props.renderConfig,
|
||||
|
@ -106,10 +109,10 @@ const areEqual = (
|
|||
if (
|
||||
prevProps.versionNonce !== nextProps.versionNonce ||
|
||||
prevProps.scale !== nextProps.scale ||
|
||||
// we need to memoize on element arrays because they may have renewed
|
||||
// we need to memoize on elementsMap because they may have renewed
|
||||
// even if versionNonce didn't change (e.g. we filter elements out based
|
||||
// on appState)
|
||||
prevProps.elements !== nextProps.elements ||
|
||||
prevProps.elementsMap !== nextProps.elementsMap ||
|
||||
prevProps.visibleElements !== nextProps.visibleElements
|
||||
) {
|
||||
return false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue