mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
perf: improve new element drawing (#8340)
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
parent
b5d7f5b4ba
commit
5e1ff7cafe
34 changed files with 749 additions and 495 deletions
|
@ -103,7 +103,9 @@ export const SelectedShapeActions = ({
|
|||
) {
|
||||
isSingleElementBoundContainer = true;
|
||||
}
|
||||
const isEditing = Boolean(appState.editingElement);
|
||||
const isEditingTextOrNewElement = Boolean(
|
||||
appState.editingTextElement || appState.newElement,
|
||||
);
|
||||
const device = useDevice();
|
||||
const isRTL = document.documentElement.getAttribute("dir") === "rtl";
|
||||
|
||||
|
@ -233,7 +235,7 @@ export const SelectedShapeActions = ({
|
|||
</div>
|
||||
</fieldset>
|
||||
)}
|
||||
{!isEditing && targetElements.length > 0 && (
|
||||
{!isEditingTextOrNewElement && targetElements.length > 0 && (
|
||||
<fieldset>
|
||||
<legend>{t("labels.actions")}</legend>
|
||||
<div className="buttonList">
|
||||
|
|
|
@ -432,6 +432,7 @@ import { getShortcutFromShortcutName } from "../actions/shortcuts";
|
|||
import { actionTextAutoResize } from "../actions/actionTextAutoResize";
|
||||
import { getVisibleSceneBounds } from "../element/bounds";
|
||||
import { isMaybeMermaidDefinition } from "../mermaid";
|
||||
import NewElementCanvas from "./canvases/NewElementCanvas";
|
||||
import { mutateElbowArrow } from "../element/routing";
|
||||
import {
|
||||
FlowChartCreator,
|
||||
|
@ -1472,25 +1473,21 @@ class App extends React.Component<AppProps, AppState> {
|
|||
scrollY: this.state.scrollY,
|
||||
height: this.state.height,
|
||||
width: this.state.width,
|
||||
editingElement: this.state.editingElement,
|
||||
editingTextElement: this.state.editingTextElement,
|
||||
newElementId: this.state.newElement?.id,
|
||||
pendingImageElementId: this.state.pendingImageElementId,
|
||||
});
|
||||
|
||||
const allElementsMap = this.scene.getNonDeletedElementsMap();
|
||||
|
||||
const shouldBlockPointerEvents =
|
||||
!(
|
||||
this.state.editingElement && isLinearElement(this.state.editingElement)
|
||||
) &&
|
||||
(this.state.selectionElement ||
|
||||
this.state.newElement ||
|
||||
this.state.selectedElementsAreBeingDragged ||
|
||||
this.state.resizingElement ||
|
||||
(this.state.activeTool.type === "laser" &&
|
||||
// technically we can just test on this once we make it more safe
|
||||
this.state.cursorButton === "down") ||
|
||||
(this.state.editingElement &&
|
||||
!isTextElement(this.state.editingElement)));
|
||||
this.state.selectionElement ||
|
||||
this.state.newElement ||
|
||||
this.state.selectedElementsAreBeingDragged ||
|
||||
this.state.resizingElement ||
|
||||
(this.state.activeTool.type === "laser" &&
|
||||
// technically we can just test on this once we make it more safe
|
||||
this.state.cursorButton === "down");
|
||||
|
||||
const firstSelectedElement = selectedElements[0];
|
||||
|
||||
|
@ -1697,6 +1694,27 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.flowChartCreator.pendingNodes,
|
||||
}}
|
||||
/>
|
||||
{this.state.newElement && (
|
||||
<NewElementCanvas
|
||||
appState={this.state}
|
||||
scale={window.devicePixelRatio}
|
||||
rc={this.rc}
|
||||
elementsMap={elementsMap}
|
||||
allElementsMap={allElementsMap}
|
||||
renderConfig={{
|
||||
imageCache: this.imageCache,
|
||||
isExporting: false,
|
||||
renderGrid: false,
|
||||
canvasBackgroundColor:
|
||||
this.state.viewBackgroundColor,
|
||||
embedsValidationStatus:
|
||||
this.embedsValidationStatus,
|
||||
elementsPendingErasure:
|
||||
this.elementsPendingErasure,
|
||||
pendingFlowchartNodes: null,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<InteractiveCanvas
|
||||
containerRef={this.excalidrawContainerRef}
|
||||
canvas={this.interactiveCanvas}
|
||||
|
@ -2073,7 +2091,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
let didUpdate = false;
|
||||
|
||||
let editingElement: AppState["editingElement"] | null = null;
|
||||
let editingTextElement: AppState["editingTextElement"] | null = null;
|
||||
if (actionResult.elements) {
|
||||
this.scene.replaceAllElements(actionResult.elements);
|
||||
didUpdate = true;
|
||||
|
@ -2086,7 +2104,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.addNewImagesToImageCache();
|
||||
}
|
||||
|
||||
if (actionResult.appState || editingElement || this.state.contextMenu) {
|
||||
if (actionResult.appState || editingTextElement || this.state.contextMenu) {
|
||||
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||
let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
|
||||
const theme =
|
||||
|
@ -2102,23 +2120,24 @@ class App extends React.Component<AppProps, AppState> {
|
|||
zenModeEnabled = this.props.zenModeEnabled;
|
||||
}
|
||||
|
||||
editingElement = actionResult.appState?.editingElement || null;
|
||||
editingTextElement = actionResult.appState?.editingTextElement || null;
|
||||
|
||||
// make sure editingElement points to latest element reference
|
||||
if (actionResult.elements && editingElement) {
|
||||
// make sure editingTextElement points to latest element reference
|
||||
if (actionResult.elements && editingTextElement) {
|
||||
actionResult.elements.forEach((element) => {
|
||||
if (
|
||||
editingElement?.id === element.id &&
|
||||
editingElement !== element &&
|
||||
isNonDeletedElement(element)
|
||||
editingTextElement?.id === element.id &&
|
||||
editingTextElement !== element &&
|
||||
isNonDeletedElement(element) &&
|
||||
isTextElement(element)
|
||||
) {
|
||||
editingElement = element;
|
||||
editingTextElement = element;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (editingElement?.isDeleted) {
|
||||
editingElement = null;
|
||||
if (editingTextElement?.isDeleted) {
|
||||
editingTextElement = null;
|
||||
}
|
||||
|
||||
this.setState((state) => {
|
||||
|
@ -2130,7 +2149,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
// or programmatically from the host, so it will need to be
|
||||
// rewritten later
|
||||
contextMenu: null,
|
||||
editingElement,
|
||||
editingTextElement,
|
||||
viewModeEnabled,
|
||||
zenModeEnabled,
|
||||
theme,
|
||||
|
@ -2709,9 +2728,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
// failsafe in case the state is being updated in incorrect order resulting
|
||||
// in the editingElement being now a deleted element
|
||||
if (this.state.editingElement?.isDeleted) {
|
||||
this.setState({ editingElement: null });
|
||||
// in the editingTextElement being now a deleted element
|
||||
if (this.state.editingTextElement?.isDeleted) {
|
||||
this.setState({ editingTextElement: null });
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -2767,7 +2786,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
const scrolledOutside =
|
||||
// hide when editing text
|
||||
isTextElement(this.state.editingElement)
|
||||
this.state.editingTextElement
|
||||
? false
|
||||
: !atLeastOneVisibleElement && elementsMap.size > 0;
|
||||
if (this.state.scrolledOutside !== scrolledOutside) {
|
||||
|
@ -4636,7 +4655,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
this.setState({
|
||||
newElement: null,
|
||||
editingElement: null,
|
||||
editingTextElement: null,
|
||||
});
|
||||
if (this.state.activeTool.locked) {
|
||||
setCursorForShape(this.interactiveCanvas, this.state);
|
||||
|
@ -5010,7 +5029,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}),
|
||||
});
|
||||
}
|
||||
this.setState({ editingElement: element });
|
||||
this.setState({ editingTextElement: element });
|
||||
|
||||
if (!existingTextElement) {
|
||||
if (container && shouldBindToContainer) {
|
||||
|
@ -5467,9 +5486,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||
lastPoint[1],
|
||||
) >= LINE_CONFIRM_THRESHOLD
|
||||
) {
|
||||
mutateElement(multiElement, {
|
||||
points: [...points, [scenePointerX - rx, scenePointerY - ry]],
|
||||
});
|
||||
mutateElement(
|
||||
multiElement,
|
||||
{
|
||||
points: [...points, [scenePointerX - rx, scenePointerY - ry]],
|
||||
},
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||
// in this branch, we're inside the commit zone, and no uncommitted
|
||||
|
@ -5486,9 +5509,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||
) < LINE_CONFIRM_THRESHOLD
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||
mutateElement(multiElement, {
|
||||
points: points.slice(0, -1),
|
||||
});
|
||||
mutateElement(
|
||||
multiElement,
|
||||
{
|
||||
points: points.slice(0, -1),
|
||||
},
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
const [gridX, gridY] = getGridPoint(
|
||||
scenePointerX,
|
||||
|
@ -5534,20 +5561,30 @@ class App extends React.Component<AppProps, AppState> {
|
|||
undefined,
|
||||
{
|
||||
isDragging: true,
|
||||
informMutation: false,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// update last uncommitted point
|
||||
mutateElement(multiElement, {
|
||||
points: [
|
||||
...points.slice(0, -1),
|
||||
[
|
||||
lastCommittedX + dxFromLastCommitted,
|
||||
lastCommittedY + dyFromLastCommitted,
|
||||
mutateElement(
|
||||
multiElement,
|
||||
{
|
||||
points: [
|
||||
...points.slice(0, -1),
|
||||
[
|
||||
lastCommittedX + dxFromLastCommitted,
|
||||
lastCommittedY + dyFromLastCommitted,
|
||||
],
|
||||
],
|
||||
],
|
||||
});
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// in this path, we're mutating multiElement to reflect
|
||||
// how it will be after adding pointer position as the next point
|
||||
// trigger update here so that new element canvas renders again to reflect this
|
||||
this.triggerRender(false);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -5950,7 +5987,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
: {}),
|
||||
appState: {
|
||||
newElement: null,
|
||||
editingElement: null,
|
||||
editingTextElement: null,
|
||||
startBoundElement: null,
|
||||
suggestedBindings: [],
|
||||
selectedElementIds: makeNextSelectedElementIds(
|
||||
|
@ -6133,7 +6170,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
this.setState({
|
||||
newElement: pendingImageElement as ExcalidrawNonSelectionElement,
|
||||
editingElement: pendingImageElement,
|
||||
pendingImageElementId: null,
|
||||
multiElement: null,
|
||||
});
|
||||
|
@ -6339,7 +6375,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
isHandToolActive(this.state) ||
|
||||
this.state.viewModeEnabled)
|
||||
) ||
|
||||
isTextElement(this.state.editingElement)
|
||||
this.state.editingTextElement
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -6883,7 +6919,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
// if we're currently still editing text, clicking outside
|
||||
// should only finalize it, not create another (irrespective
|
||||
// of state.activeTool.locked)
|
||||
if (isTextElement(this.state.editingElement)) {
|
||||
if (this.state.editingTextElement) {
|
||||
return;
|
||||
}
|
||||
let sceneX = pointerDownState.origin.x;
|
||||
|
@ -6934,6 +6970,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
y: gridY,
|
||||
});
|
||||
|
||||
const simulatePressure = event.pressure === 0.5;
|
||||
|
||||
const element = newFreeDrawElement({
|
||||
type: elementType,
|
||||
x: gridX,
|
||||
|
@ -6946,11 +6984,15 @@ class App extends React.Component<AppProps, AppState> {
|
|||
roughness: this.state.currentItemRoughness,
|
||||
opacity: this.state.currentItemOpacity,
|
||||
roundness: null,
|
||||
simulatePressure: event.pressure === 0.5,
|
||||
simulatePressure,
|
||||
locked: false,
|
||||
frameId: topLayerFrame ? topLayerFrame.id : null,
|
||||
points: [[0, 0]],
|
||||
pressures: simulatePressure ? [] : [event.pressure],
|
||||
});
|
||||
|
||||
this.scene.insertElement(element);
|
||||
|
||||
this.setState((prevState) => {
|
||||
const nextSelectedElementIds = {
|
||||
...prevState.selectedElementIds,
|
||||
|
@ -6964,21 +7006,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||
};
|
||||
});
|
||||
|
||||
const pressures = element.simulatePressure
|
||||
? element.pressures
|
||||
: [...element.pressures, event.pressure];
|
||||
|
||||
mutateElement(element, {
|
||||
points: [[0, 0]],
|
||||
pressures,
|
||||
});
|
||||
|
||||
const boundElement = getHoveredElementForBinding(
|
||||
pointerDownState.origin,
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
this.scene.insertElement(element);
|
||||
|
||||
this.setState({
|
||||
newElement: element,
|
||||
startBoundElement: boundElement,
|
||||
|
@ -7279,7 +7312,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.scene.insertElement(element);
|
||||
this.setState({
|
||||
newElement: element,
|
||||
editingElement: element,
|
||||
startBoundElement: boundElement,
|
||||
suggestedBindings: [],
|
||||
});
|
||||
|
@ -7671,11 +7703,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
// prevent dragging even if we're no longer holding cmd/ctrl otherwise
|
||||
// it would have weird results (stuff jumping all over the screen)
|
||||
// Checking for editingElement to avoid jump while editing on mobile #6503
|
||||
// Checking for editingTextElement to avoid jump while editing on mobile #6503
|
||||
if (
|
||||
selectedElements.length > 0 &&
|
||||
!pointerDownState.withCmdOrCtrl &&
|
||||
!this.state.editingElement &&
|
||||
!this.state.editingTextElement &&
|
||||
this.state.activeEmbeddable?.state !== "active"
|
||||
) {
|
||||
const dragOffset = {
|
||||
|
@ -7870,9 +7902,17 @@ class App extends React.Component<AppProps, AppState> {
|
|||
? newElement.pressures
|
||||
: [...newElement.pressures, event.pressure];
|
||||
|
||||
mutateElement(newElement, {
|
||||
points: [...points, [dx, dy]],
|
||||
pressures,
|
||||
mutateElement(
|
||||
newElement,
|
||||
{
|
||||
points: [...points, [dx, dy]],
|
||||
pressures,
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
this.setState({
|
||||
newElement,
|
||||
});
|
||||
}
|
||||
} else if (isLinearElement(newElement)) {
|
||||
|
@ -7891,9 +7931,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (points.length === 1) {
|
||||
mutateElement(newElement, {
|
||||
points: [...points, [dx, dy]],
|
||||
});
|
||||
mutateElement(
|
||||
newElement,
|
||||
{
|
||||
points: [...points, [dx, dy]],
|
||||
},
|
||||
false,
|
||||
);
|
||||
} else if (points.length > 1 && isElbowArrow(newElement)) {
|
||||
mutateElbowArrow(
|
||||
newElement,
|
||||
|
@ -7903,14 +7947,23 @@ class App extends React.Component<AppProps, AppState> {
|
|||
undefined,
|
||||
{
|
||||
isDragging: true,
|
||||
informMutation: false,
|
||||
},
|
||||
);
|
||||
} else if (points.length === 2) {
|
||||
mutateElement(newElement, {
|
||||
points: [...points.slice(0, -1), [dx, dy]],
|
||||
});
|
||||
mutateElement(
|
||||
newElement,
|
||||
{
|
||||
points: [...points.slice(0, -1), [dx, dy]],
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
newElement,
|
||||
});
|
||||
|
||||
if (isBindingElement(newElement, false)) {
|
||||
// When creating a linear element by dragging
|
||||
this.maybeSuggestBindingsForLinearElementAtCoords(
|
||||
|
@ -7922,7 +7975,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
} else {
|
||||
pointerDownState.lastCoords.x = pointerCoords.x;
|
||||
pointerDownState.lastCoords.y = pointerCoords.y;
|
||||
this.maybeDragNewGenericElement(pointerDownState, event);
|
||||
this.maybeDragNewGenericElement(pointerDownState, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8080,12 +8133,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
frameToHighlight: null,
|
||||
elementsToHighlight: null,
|
||||
cursorButton: "up",
|
||||
// text elements are reset on finalize, and resetting on pointerup
|
||||
// may cause issues with double taps
|
||||
editingElement:
|
||||
multiElement || isTextElement(this.state.editingElement)
|
||||
? this.state.editingElement
|
||||
: null,
|
||||
snapLines: updateStable(prevState.snapLines, []),
|
||||
originSnapOffset: null,
|
||||
}));
|
||||
|
@ -8270,7 +8317,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
});
|
||||
this.setState({
|
||||
multiElement: newElement,
|
||||
editingElement: this.state.newElement,
|
||||
newElement,
|
||||
});
|
||||
} else if (pointerDownState.drag.hasOccurred && !multiElement) {
|
||||
if (
|
||||
|
@ -8307,6 +8354,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
newElement: null,
|
||||
}));
|
||||
}
|
||||
// so that the scene gets rendered again to display the newly drawn linear as well
|
||||
this.scene.triggerUpdate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -8371,6 +8420,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
if (newElement) {
|
||||
mutateElement(newElement, getNormalizedDimensions(newElement));
|
||||
// the above does not guarantee the scene to be rendered again, hence the trigger below
|
||||
this.scene.triggerUpdate();
|
||||
}
|
||||
|
||||
if (pointerDownState.drag.hasOccurred) {
|
||||
|
@ -9179,7 +9230,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.setState(
|
||||
{
|
||||
pendingImageElementId: null,
|
||||
editingElement: null,
|
||||
newElement: null,
|
||||
activeTool: updateActiveTool(this.state, { type: "selection" }),
|
||||
},
|
||||
() => {
|
||||
|
@ -9675,23 +9726,25 @@ class App extends React.Component<AppProps, AppState> {
|
|||
private maybeDragNewGenericElement = (
|
||||
pointerDownState: PointerDownState,
|
||||
event: MouseEvent | KeyboardEvent,
|
||||
informMutation = true,
|
||||
): void => {
|
||||
const selectionElement = this.state.selectionElement;
|
||||
const pointerCoords = pointerDownState.lastCoords;
|
||||
if (selectionElement && this.state.activeTool.type !== "eraser") {
|
||||
dragNewElement(
|
||||
selectionElement,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState.origin.x,
|
||||
pointerDownState.origin.y,
|
||||
pointerCoords.x,
|
||||
pointerCoords.y,
|
||||
distance(pointerDownState.origin.x, pointerCoords.x),
|
||||
distance(pointerDownState.origin.y, pointerCoords.y),
|
||||
shouldMaintainAspectRatio(event),
|
||||
shouldResizeFromCenter(event),
|
||||
this.state.zoom.value,
|
||||
);
|
||||
dragNewElement({
|
||||
newElement: selectionElement,
|
||||
elementType: this.state.activeTool.type,
|
||||
originX: pointerDownState.origin.x,
|
||||
originY: pointerDownState.origin.y,
|
||||
x: pointerCoords.x,
|
||||
y: pointerCoords.y,
|
||||
width: distance(pointerDownState.origin.x, pointerCoords.x),
|
||||
height: distance(pointerDownState.origin.y, pointerCoords.y),
|
||||
shouldMaintainAspectRatio: shouldMaintainAspectRatio(event),
|
||||
shouldResizeFromCenter: shouldResizeFromCenter(event),
|
||||
zoom: this.state.zoom.value,
|
||||
informMutation,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9740,23 +9793,28 @@ class App extends React.Component<AppProps, AppState> {
|
|||
snapLines,
|
||||
});
|
||||
|
||||
dragNewElement(
|
||||
dragNewElement({
|
||||
newElement,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState.originInGrid.x,
|
||||
pointerDownState.originInGrid.y,
|
||||
gridX,
|
||||
gridY,
|
||||
distance(pointerDownState.originInGrid.x, gridX),
|
||||
distance(pointerDownState.originInGrid.y, gridY),
|
||||
isImageElement(newElement)
|
||||
elementType: this.state.activeTool.type,
|
||||
originX: pointerDownState.originInGrid.x,
|
||||
originY: pointerDownState.originInGrid.y,
|
||||
x: gridX,
|
||||
y: gridY,
|
||||
width: distance(pointerDownState.originInGrid.x, gridX),
|
||||
height: distance(pointerDownState.originInGrid.y, gridY),
|
||||
shouldMaintainAspectRatio: isImageElement(newElement)
|
||||
? !shouldMaintainAspectRatio(event)
|
||||
: shouldMaintainAspectRatio(event),
|
||||
shouldResizeFromCenter(event),
|
||||
this.state.zoom.value,
|
||||
aspectRatio,
|
||||
this.state.originSnapOffset,
|
||||
);
|
||||
shouldResizeFromCenter: shouldResizeFromCenter(event),
|
||||
zoom: this.state.zoom.value,
|
||||
widthAspectRatio: aspectRatio,
|
||||
originOffset: this.state.originSnapOffset,
|
||||
informMutation,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
newElement,
|
||||
});
|
||||
|
||||
// highlight elements that are to be added to frames on frames creation
|
||||
if (
|
||||
|
|
|
@ -87,7 +87,7 @@ const getHints = ({
|
|||
return t("hints.text_selected");
|
||||
}
|
||||
|
||||
if (appState.editingElement && isTextElement(appState.editingElement)) {
|
||||
if (appState.editingTextElement) {
|
||||
return t("hints.text_editing");
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ const getHints = ({
|
|||
if (
|
||||
appState.selectionElement &&
|
||||
!selectedElements.length &&
|
||||
!appState.editingElement &&
|
||||
!appState.editingTextElement &&
|
||||
!appState.editingLinearElement
|
||||
) {
|
||||
return t("hints.deepBoxSelect");
|
||||
|
|
|
@ -202,7 +202,7 @@ const getRelevantAppStateProps = (
|
|||
activeEmbeddable: appState.activeEmbeddable,
|
||||
snapLines: appState.snapLines,
|
||||
zenModeEnabled: appState.zenModeEnabled,
|
||||
editingElement: appState.editingElement,
|
||||
editingTextElement: appState.editingTextElement,
|
||||
});
|
||||
|
||||
const areEqual = (
|
||||
|
|
56
packages/excalidraw/components/canvases/NewElementCanvas.tsx
Normal file
56
packages/excalidraw/components/canvases/NewElementCanvas.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import type { NonDeletedSceneElementsMap } from "../../element/types";
|
||||
import type { AppState } from "../../types";
|
||||
import type {
|
||||
RenderableElementsMap,
|
||||
StaticCanvasRenderConfig,
|
||||
} from "../../scene/types";
|
||||
import type { RoughCanvas } from "roughjs/bin/canvas";
|
||||
import { renderNewElementScene } from "../../renderer/renderNewElementScene";
|
||||
import { isRenderThrottlingEnabled } from "../../reactUtils";
|
||||
|
||||
interface NewElementCanvasProps {
|
||||
appState: AppState;
|
||||
elementsMap: RenderableElementsMap;
|
||||
allElementsMap: NonDeletedSceneElementsMap;
|
||||
scale: number;
|
||||
rc: RoughCanvas;
|
||||
renderConfig: StaticCanvasRenderConfig;
|
||||
}
|
||||
|
||||
const NewElementCanvas = (props: NewElementCanvasProps) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) {
|
||||
return;
|
||||
}
|
||||
renderNewElementScene(
|
||||
{
|
||||
canvas: canvasRef.current,
|
||||
scale: props.scale,
|
||||
newElement: props.appState.newElement,
|
||||
elementsMap: props.elementsMap,
|
||||
allElementsMap: props.allElementsMap,
|
||||
rc: props.rc,
|
||||
renderConfig: props.renderConfig,
|
||||
appState: props.appState,
|
||||
},
|
||||
isRenderThrottlingEnabled(),
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<canvas
|
||||
className="excalidraw__canvas"
|
||||
style={{
|
||||
width: props.appState.width,
|
||||
height: props.appState.height,
|
||||
}}
|
||||
width={props.appState.width * props.scale}
|
||||
height={props.appState.height * props.scale}
|
||||
ref={canvasRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewElementCanvas;
|
Loading…
Add table
Add a link
Reference in a new issue