mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Merge remote-tracking branch 'origin/master' into aakansha-custom-elements
Update customType
This commit is contained in:
commit
3d459076fb
25 changed files with 381 additions and 229 deletions
|
@ -332,7 +332,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||
lastPointerUp: React.PointerEvent<HTMLElement> | PointerEvent | null = null;
|
||||
contextMenuOpen: boolean = false;
|
||||
lastScenePointer: { x: number; y: number } | null = null;
|
||||
customElementName: string | null = null;
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
|
@ -414,18 +413,17 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.actionManager.registerAction(createRedoAction(this.history));
|
||||
}
|
||||
|
||||
setCustomType = (name: string) => {
|
||||
this.setState({ elementType: "custom" });
|
||||
this.customElementName = name;
|
||||
setCustomType = (customType: string) => {
|
||||
this.setState({ activeTool: { type: "custom", customType } });
|
||||
};
|
||||
|
||||
renderCustomElement = (
|
||||
coords: { x: number; y: number },
|
||||
customType: string = "",
|
||||
) => {
|
||||
renderCustomElement = (coords: { x: number; y: number }) => {
|
||||
if (this.state.activeTool.type !== "custom") {
|
||||
return;
|
||||
}
|
||||
const config = getCustomElementConfig(
|
||||
this.props.customElementsConfig,
|
||||
customType,
|
||||
this.state.activeTool.customType,
|
||||
);
|
||||
if (!config) {
|
||||
return;
|
||||
|
@ -438,7 +436,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
const width = config.width || 40;
|
||||
const height = config.height || 40;
|
||||
const customElement = newCustomElement(customType, {
|
||||
const customElement = newCustomElement(this.state.activeTool.customType, {
|
||||
type: "custom",
|
||||
x: gridX - width / 2,
|
||||
y: gridY - height / 2,
|
||||
|
@ -864,10 +862,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
scene.appState = {
|
||||
...scene.appState,
|
||||
elementType:
|
||||
scene.appState.elementType === "image"
|
||||
? "selection"
|
||||
: scene.appState.elementType,
|
||||
activeTool:
|
||||
scene.appState.activeTool.type === "image"
|
||||
? { type: "selection" }
|
||||
: scene.appState.activeTool,
|
||||
isLoading: false,
|
||||
};
|
||||
if (initialData?.scrollToContent) {
|
||||
|
@ -1121,15 +1119,15 @@ class App extends React.Component<AppProps, AppState> {
|
|||
Object.keys(this.state.selectedElementIds).length &&
|
||||
isEraserActive(this.state)
|
||||
) {
|
||||
this.setState({ elementType: "selection" });
|
||||
this.setState({ activeTool: { type: "selection" } });
|
||||
}
|
||||
if (prevState.theme !== this.state.theme) {
|
||||
setEraserCursor(this.canvas, this.state.theme);
|
||||
}
|
||||
// Hide hyperlink popup if shown when element type is not selection
|
||||
if (
|
||||
prevState.elementType === "selection" &&
|
||||
this.state.elementType !== "selection" &&
|
||||
prevState.activeTool.type === "selection" &&
|
||||
this.state.activeTool.type !== "selection" &&
|
||||
this.state.showHyperlinkPopup
|
||||
) {
|
||||
this.setState({ showHyperlinkPopup: false });
|
||||
|
@ -1183,7 +1181,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
const { multiElement } = prevState;
|
||||
if (
|
||||
prevState.elementType !== this.state.elementType &&
|
||||
prevState.activeTool !== this.state.activeTool &&
|
||||
multiElement != null &&
|
||||
isBindingEnabled(this.state) &&
|
||||
isBindingElement(multiElement)
|
||||
|
@ -1487,7 +1485,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
} else if (data.text) {
|
||||
this.addTextFromPaste(data.text);
|
||||
}
|
||||
this.selectShapeTool("selection");
|
||||
this.setActiveTool({ type: "selection" });
|
||||
event?.preventDefault();
|
||||
},
|
||||
);
|
||||
|
@ -1575,7 +1573,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
},
|
||||
);
|
||||
this.selectShapeTool("selection");
|
||||
this.setActiveTool({ type: "selection" });
|
||||
};
|
||||
|
||||
private addTextFromPaste(text: any) {
|
||||
|
@ -1628,9 +1626,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.setState((prevState) => {
|
||||
return {
|
||||
elementLocked: !prevState.elementLocked,
|
||||
elementType: prevState.elementLocked
|
||||
? "selection"
|
||||
: prevState.elementType,
|
||||
activeTool: prevState.elementLocked
|
||||
? { type: "selection" }
|
||||
: prevState.activeTool,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -1905,7 +1903,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
) {
|
||||
const shape = findShapeByKey(event.key);
|
||||
if (shape) {
|
||||
this.selectShapeTool(shape);
|
||||
this.setActiveTool({ type: shape });
|
||||
} else if (event.key === KEYS.Q) {
|
||||
this.toggleLock();
|
||||
}
|
||||
|
@ -1922,7 +1920,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.state,
|
||||
);
|
||||
if (
|
||||
this.state.elementType === "selection" &&
|
||||
this.state.activeTool.type === "selection" &&
|
||||
!selectedElements.length
|
||||
) {
|
||||
return;
|
||||
|
@ -1930,7 +1928,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
if (
|
||||
event.key === KEYS.G &&
|
||||
(hasBackground(this.state.elementType) ||
|
||||
(hasBackground(this.state.activeTool.type) ||
|
||||
selectedElements.some((element) => hasBackground(element.type)))
|
||||
) {
|
||||
this.setState({ openPopup: "backgroundColorPicker" });
|
||||
|
@ -1946,7 +1944,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
if (event.key === KEYS.SPACE) {
|
||||
if (this.state.viewModeEnabled) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.GRAB);
|
||||
} else if (this.state.elementType === "selection") {
|
||||
} else if (this.state.activeTool.type === "selection") {
|
||||
resetCursor(this.canvas);
|
||||
} else {
|
||||
setCursorForShape(this.canvas, this.state);
|
||||
|
@ -1973,28 +1971,28 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
});
|
||||
|
||||
private selectShapeTool(elementType: AppState["elementType"]) {
|
||||
private setActiveTool(tool: AppState["activeTool"]) {
|
||||
if (!isHoldingSpace) {
|
||||
setCursorForShape(this.canvas, this.state);
|
||||
}
|
||||
if (isToolIcon(document.activeElement)) {
|
||||
this.focusContainer();
|
||||
}
|
||||
if (!isLinearElementType(elementType)) {
|
||||
if (!isLinearElementType(tool.type)) {
|
||||
this.setState({ suggestedBindings: [] });
|
||||
}
|
||||
if (elementType === "image") {
|
||||
if (tool.type === "image") {
|
||||
this.onImageAction();
|
||||
}
|
||||
if (elementType !== "selection") {
|
||||
if (tool.type !== "selection") {
|
||||
this.setState({
|
||||
elementType,
|
||||
activeTool: tool,
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
editingGroupId: null,
|
||||
});
|
||||
} else {
|
||||
this.setState({ elementType });
|
||||
this.setState({ activeTool: tool });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2368,7 +2366,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
return;
|
||||
}
|
||||
// we should only be able to double click when mode is selection
|
||||
if (this.state.elementType !== "selection") {
|
||||
if (this.state.activeTool.type !== "selection") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2558,7 +2556,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
const distance = getDistance(Array.from(gesture.pointers.values()));
|
||||
const scaleFactor =
|
||||
this.state.elementType === "freedraw" && this.state.penMode
|
||||
this.state.activeTool.type === "freedraw" && this.state.penMode
|
||||
? 1
|
||||
: distance / gesture.initialDistance;
|
||||
|
||||
|
@ -2633,7 +2631,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
}
|
||||
|
||||
if (isBindingElementType(this.state.elementType)) {
|
||||
if (isBindingElementType(this.state.activeTool.type)) {
|
||||
// Hovering with a selected tool or creating new linear element via click
|
||||
// and point
|
||||
const { draggingElement } = this.state;
|
||||
|
@ -2709,9 +2707,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
const hasDeselectedButton = Boolean(event.buttons);
|
||||
if (
|
||||
hasDeselectedButton ||
|
||||
(this.state.elementType !== "selection" &&
|
||||
this.state.elementType !== "text" &&
|
||||
this.state.elementType !== "eraser")
|
||||
(this.state.activeTool.type !== "selection" &&
|
||||
this.state.activeTool.type !== "text" &&
|
||||
this.state.activeTool.type !== "eraser")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -2788,7 +2786,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
!this.state.showHyperlinkPopup
|
||||
) {
|
||||
this.setState({ showHyperlinkPopup: "info" });
|
||||
} else if (this.state.elementType === "text") {
|
||||
} else if (this.state.activeTool.type === "text") {
|
||||
setCursor(
|
||||
this.canvas,
|
||||
isTextElement(hitElement) ? CURSOR_TYPE.TEXT : CURSOR_TYPE.CROSSHAIR,
|
||||
|
@ -3000,27 +2998,27 @@ class App extends React.Component<AppProps, AppState> {
|
|||
const allowOnPointerDown =
|
||||
!this.state.penMode ||
|
||||
event.pointerType !== "touch" ||
|
||||
this.state.elementType === "selection" ||
|
||||
this.state.elementType === "text" ||
|
||||
this.state.elementType === "image";
|
||||
this.state.activeTool.type === "selection" ||
|
||||
this.state.activeTool.type === "text" ||
|
||||
this.state.activeTool.type === "image";
|
||||
|
||||
if (!allowOnPointerDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.elementType === "text") {
|
||||
if (this.state.activeTool.type === "text") {
|
||||
this.handleTextOnPointerDown(event, pointerDownState);
|
||||
return;
|
||||
} else if (
|
||||
this.state.elementType === "arrow" ||
|
||||
this.state.elementType === "line"
|
||||
this.state.activeTool.type === "arrow" ||
|
||||
this.state.activeTool.type === "line"
|
||||
) {
|
||||
this.handleLinearElementOnPointerDown(
|
||||
event,
|
||||
this.state.elementType,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState,
|
||||
);
|
||||
} else if (this.state.elementType === "image") {
|
||||
} else if (this.state.activeTool.type === "image") {
|
||||
// reset image preview on pointerdown
|
||||
setCursor(this.canvas, CURSOR_TYPE.CROSSHAIR);
|
||||
|
||||
|
@ -3040,26 +3038,21 @@ class App extends React.Component<AppProps, AppState> {
|
|||
x,
|
||||
y,
|
||||
});
|
||||
} else if (this.state.elementType === "custom") {
|
||||
if (this.customElementName) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.CROSSHAIR);
|
||||
this.renderCustomElement(
|
||||
{
|
||||
x: pointerDownState.origin.x,
|
||||
y: pointerDownState.origin.y,
|
||||
},
|
||||
this.customElementName,
|
||||
);
|
||||
}
|
||||
} else if (this.state.elementType === "freedraw") {
|
||||
} else if (this.state.activeTool.type === "custom") {
|
||||
setCursor(this.canvas, CURSOR_TYPE.CROSSHAIR);
|
||||
this.renderCustomElement({
|
||||
x: pointerDownState.origin.x,
|
||||
y: pointerDownState.origin.y,
|
||||
});
|
||||
} else if (this.state.activeTool.type === "freedraw") {
|
||||
this.handleFreeDrawElementOnPointerDown(
|
||||
event,
|
||||
this.state.elementType,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState,
|
||||
);
|
||||
} else if (this.state.elementType !== "eraser") {
|
||||
} else if (this.state.activeTool.type !== "eraser") {
|
||||
this.createGenericElementOnPointerDown(
|
||||
this.state.elementType,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState,
|
||||
);
|
||||
}
|
||||
|
@ -3111,7 +3104,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (
|
||||
this.state.elementType === "selection" &&
|
||||
this.state.activeTool.type === "selection" &&
|
||||
this.props.onElementClick &&
|
||||
hitElement
|
||||
) {
|
||||
|
@ -3392,7 +3385,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
private clearSelectionIfNotUsingSelection = (): void => {
|
||||
if (this.state.elementType !== "selection") {
|
||||
if (this.state.activeTool.type !== "selection") {
|
||||
this.setState({
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
|
@ -3408,7 +3401,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
pointerDownState: PointerDownState,
|
||||
): boolean => {
|
||||
if (this.state.elementType === "selection") {
|
||||
if (this.state.activeTool.type === "selection") {
|
||||
const elements = this.scene.getElements();
|
||||
const selectedElements = getSelectedElements(elements, this.state);
|
||||
if (selectedElements.length === 1 && !this.state.editingLinearElement) {
|
||||
|
@ -3662,7 +3655,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
resetCursor(this.canvas);
|
||||
if (!this.state.elementLocked) {
|
||||
this.setState({
|
||||
elementType: "selection",
|
||||
activeTool: { type: "selection" },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -3971,8 +3964,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||
// triggering pointermove)
|
||||
if (
|
||||
!pointerDownState.drag.hasOccurred &&
|
||||
(this.state.elementType === "arrow" ||
|
||||
this.state.elementType === "line")
|
||||
(this.state.activeTool.type === "arrow" ||
|
||||
this.state.activeTool.type === "line")
|
||||
) {
|
||||
if (
|
||||
distance2d(
|
||||
|
@ -4167,7 +4160,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
if (shouldRotateWithDiscreteAngle(event) && points.length === 2) {
|
||||
({ width: dx, height: dy } = getPerfectElementSize(
|
||||
this.state.elementType,
|
||||
this.state.activeTool.type,
|
||||
dx,
|
||||
dy,
|
||||
));
|
||||
|
@ -4195,7 +4188,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.maybeDragNewGenericElement(pointerDownState, event);
|
||||
}
|
||||
|
||||
if (this.state.elementType === "selection") {
|
||||
if (this.state.activeTool.type === "selection") {
|
||||
pointerDownState.boxSelection.hasOccurred = true;
|
||||
|
||||
const elements = this.scene.getElements();
|
||||
|
@ -4307,7 +4300,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
draggingElement,
|
||||
resizingElement,
|
||||
multiElement,
|
||||
elementType,
|
||||
activeTool,
|
||||
elementLocked,
|
||||
isResizing,
|
||||
isRotating,
|
||||
|
@ -4476,7 +4469,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
resetCursor(this.canvas);
|
||||
this.setState((prevState) => ({
|
||||
draggingElement: null,
|
||||
elementType: "selection",
|
||||
activeTool: { type: "selection" },
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[this.state.draggingElement!.id]: true,
|
||||
|
@ -4496,7 +4489,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (
|
||||
elementType !== "selection" &&
|
||||
activeTool.type !== "selection" &&
|
||||
draggingElement &&
|
||||
isInvisiblySmallElement(draggingElement)
|
||||
) {
|
||||
|
@ -4665,7 +4658,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!elementLocked && elementType !== "freedraw" && draggingElement) {
|
||||
if (!elementLocked && activeTool.type !== "freedraw" && draggingElement) {
|
||||
this.setState((prevState) => ({
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
|
@ -4675,7 +4668,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (
|
||||
elementType !== "selection" ||
|
||||
activeTool.type !== "selection" ||
|
||||
isSomeElementSelected(this.scene.getElements(), this.state)
|
||||
) {
|
||||
this.history.resumeRecording();
|
||||
|
@ -4689,12 +4682,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||
);
|
||||
}
|
||||
|
||||
if (!elementLocked && elementType !== "freedraw") {
|
||||
if (!elementLocked && activeTool.type !== "freedraw") {
|
||||
resetCursor(this.canvas);
|
||||
this.setState({
|
||||
draggingElement: null,
|
||||
suggestedBindings: [],
|
||||
elementType: "selection",
|
||||
activeTool: { type: "selection" },
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
|
@ -5000,7 +4993,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
{
|
||||
pendingImageElement: null,
|
||||
editingElement: null,
|
||||
elementType: "selection",
|
||||
activeTool: { type: "selection" },
|
||||
},
|
||||
() => {
|
||||
this.actionManager.executeAction(actionFinalize);
|
||||
|
@ -5370,7 +5363,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
(event.nativeEvent.pointerType === "pen" &&
|
||||
// always allow if user uses a pen secondary button
|
||||
event.button !== POINTER_BUTTON.SECONDARY)) &&
|
||||
this.state.elementType !== "selection"
|
||||
this.state.activeTool.type !== "selection"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -5404,10 +5397,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||
if (!draggingElement) {
|
||||
return;
|
||||
}
|
||||
if (draggingElement.type === "selection") {
|
||||
if (
|
||||
draggingElement.type === "selection" &&
|
||||
this.state.activeTool.type !== "eraser"
|
||||
) {
|
||||
dragNewElement(
|
||||
draggingElement,
|
||||
this.state.elementType,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState.origin.x,
|
||||
pointerDownState.origin.y,
|
||||
pointerCoords.x,
|
||||
|
@ -5434,7 +5430,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
dragNewElement(
|
||||
draggingElement,
|
||||
this.state.elementType,
|
||||
this.state.activeTool.type,
|
||||
pointerDownState.originInGrid.x,
|
||||
pointerDownState.originInGrid.y,
|
||||
gridX,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue