mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Merge db31855e26
into 6e47fadb59
This commit is contained in:
commit
53e89bd266
8 changed files with 1853 additions and 2063 deletions
|
@ -412,3 +412,6 @@ export type NonDeletedSceneElementsMap = Map<
|
|||
export type ElementsMapOrArray =
|
||||
| readonly ExcalidrawElement[]
|
||||
| Readonly<ElementsMap>;
|
||||
|
||||
/** Supported shape types for shape conversion */
|
||||
export type ShapeType = "rectangle" | "diamond" | "ellipse";
|
||||
|
|
93
packages/excalidraw/actions/actionChangeShapeType.tsx
Normal file
93
packages/excalidraw/actions/actionChangeShapeType.tsx
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { newElementWith } from "@excalidraw/element/mutateElement";
|
||||
|
||||
import { isTextElement } from "@excalidraw/element/typeChecks";
|
||||
|
||||
import type { ExcalidrawElement } from "@excalidraw/element/types";
|
||||
import type { ShapeType } from "@excalidraw/element/src/types";
|
||||
|
||||
import { CaptureUpdateAction } from "../store";
|
||||
|
||||
import { register } from "./register";
|
||||
|
||||
import type { AppState } from "../types";
|
||||
|
||||
import type { ActionResult } from "./types";
|
||||
|
||||
const SUPPORTED_TYPES: ShapeType[] = ["rectangle", "diamond", "ellipse"];
|
||||
|
||||
const isShapeTypeSupported = (
|
||||
element: ExcalidrawElement,
|
||||
): element is ExcalidrawElement & { type: ShapeType } => {
|
||||
return SUPPORTED_TYPES.includes(element.type as ShapeType);
|
||||
};
|
||||
|
||||
export const actionChangeShapeType = register({
|
||||
name: "changeShapeType",
|
||||
label: "Change shape type",
|
||||
trackEvent: { category: "element" },
|
||||
perform: (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
targetType: ShapeType,
|
||||
): ActionResult => {
|
||||
// restrict the shape type to rectangle, diamond, ellipse
|
||||
const selectedElements = elements.filter(
|
||||
(el) => appState.selectedElementIds[el.id],
|
||||
);
|
||||
|
||||
// If no elements are selected, return false
|
||||
if (selectedElements.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter out elements that can be converted to the target shape type
|
||||
const convertibleElements = selectedElements.filter(isShapeTypeSupported);
|
||||
|
||||
// If no convertible elements, return false
|
||||
if (convertibleElements.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a map of element IDs that should be converted
|
||||
const elementsToConvert = new Set(
|
||||
convertibleElements
|
||||
.filter((element) => element.type !== targetType) // Skip elements already of target type
|
||||
.map((element) => element.id),
|
||||
);
|
||||
|
||||
// If all elements are already the target type, return false
|
||||
if (elementsToConvert.size === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map through all elements and convert the ones that need to be converted
|
||||
const newElements = elements.map((el) => {
|
||||
if (elementsToConvert.has(el.id)) {
|
||||
// don't convert text to other shapes
|
||||
if (isTextElement(el)) {
|
||||
return el;
|
||||
}
|
||||
|
||||
// Default case: just change the type
|
||||
return newElementWith(el, {
|
||||
type: targetType,
|
||||
});
|
||||
}
|
||||
return el;
|
||||
});
|
||||
|
||||
return {
|
||||
elements: newElements,
|
||||
appState: { ...appState },
|
||||
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
|
||||
};
|
||||
},
|
||||
keyTest: (event: KeyboardEvent | React.KeyboardEvent<Element>) => {
|
||||
// Handle R/D/E with Ctrl/Cmd modifier
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
const key = event.key.toLowerCase();
|
||||
return key === "r" || key === "d" || key === "e";
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
|
@ -92,3 +92,5 @@ export { actionToggleLinearEditor } from "./actionLinearEditor";
|
|||
export { actionToggleSearchMenu } from "./actionToggleSearchMenu";
|
||||
|
||||
export { actionToggleCropEditor } from "./actionCropEditor";
|
||||
|
||||
export { actionChangeShapeType } from "./actionChangeShapeType";
|
||||
|
|
|
@ -139,7 +139,8 @@ export type ActionName =
|
|||
| "copyElementLink"
|
||||
| "linkToElement"
|
||||
| "cropEditor"
|
||||
| "wrapSelectionInFrame";
|
||||
| "wrapSelectionInFrame"
|
||||
| "changeShapeType";
|
||||
|
||||
export type PanelComponentProps = {
|
||||
elements: readonly ExcalidrawElement[];
|
||||
|
|
|
@ -330,10 +330,13 @@ import type {
|
|||
|
||||
import type { ValueOf } from "@excalidraw/common/utility-types";
|
||||
|
||||
import type { ShapeType } from "@excalidraw/element/src/types";
|
||||
|
||||
import {
|
||||
actionAddToLibrary,
|
||||
actionBringForward,
|
||||
actionBringToFront,
|
||||
actionChangeShapeType,
|
||||
actionCopy,
|
||||
actionCopyAsPng,
|
||||
actionCopyAsSvg,
|
||||
|
@ -522,6 +525,7 @@ import type {
|
|||
Offsets,
|
||||
} from "../types";
|
||||
import type { RoughCanvas } from "roughjs/bin/canvas";
|
||||
|
||||
import type { Action, ActionResult } from "../actions/types";
|
||||
|
||||
const AppContext = React.createContext<AppClassProperties>(null!);
|
||||
|
@ -4133,6 +4137,31 @@ class App extends React.Component<AppProps, AppState> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Handle shape type change shortcuts
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
const key = event.key.toLowerCase();
|
||||
let targetType: ShapeType | null = null;
|
||||
|
||||
if (key === "r") {
|
||||
targetType = "rectangle";
|
||||
} else if (key === "d") {
|
||||
targetType = "diamond";
|
||||
} else if (key === "e") {
|
||||
targetType = "ellipse";
|
||||
}
|
||||
|
||||
if (targetType) {
|
||||
event.preventDefault();
|
||||
// Execute the action with the target shape type
|
||||
this.actionManager.executeAction(
|
||||
actionChangeShapeType,
|
||||
"ui",
|
||||
targetType,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const selectedElements = getSelectedElements(
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.state,
|
||||
|
|
2
packages/excalidraw/element/types.ts
Normal file
2
packages/excalidraw/element/types.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
/** Supported shape types for shape conversion */
|
||||
export type ShapeType = "rectangle" | "diamond" | "ellipse";
|
|
@ -89,7 +89,7 @@
|
|||
"jotai-scope": "0.7.2",
|
||||
"lodash.throttle": "4.1.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"nanoid": "3.3.3",
|
||||
"nanoid": "5.1.5",
|
||||
"open-color": "1.9.1",
|
||||
"pako": "2.0.3",
|
||||
"perfect-freehand": "1.2.0",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue