diff --git a/src/components/App.tsx b/src/components/App.tsx index 93d2beb74..8f0f0995d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -224,7 +224,6 @@ import { withBatchedUpdatesThrottled, updateObject, setEraserCursor, - getCustomElementConfig, } from "../utils"; import ContextMenu, { ContextMenuOption } from "./ContextMenu"; import LayerUI from "./LayerUI"; @@ -426,10 +425,9 @@ class App extends React.Component { if (this.state.activeTool.type !== "custom") { return; } - const config = getCustomElementConfig( - this.props.customElementsConfig, - this.state.activeTool.customType, - ); + const config = + this.props.customElementsConfig?.[this.state.activeTool.customType]; + if (!config) { return; } @@ -462,10 +460,9 @@ class App extends React.Component { ...this.scene.getElementsIncludingDeleted(), customElement, ]); - const customElementConfig = getCustomElementConfig( - this.props.customElementsConfig, - customElement.customType, - ); + const customElementConfig = + this.props.customElementsConfig?.[customElement.customType]; + if (customElementConfig && customElementConfig.onCreate) { customElementConfig.onCreate(customElement); } @@ -2898,10 +2895,9 @@ class App extends React.Component { !hitElement?.locked ) { if (hitElement && isCustomElement(hitElement)) { - const config = getCustomElementConfig( - this.props.customElementsConfig, - hitElement.customType, - ); + const config = + this.props.customElementsConfig?.[hitElement.customType]; + if (!config?.transformHandles) { return; } @@ -5465,10 +5461,8 @@ class App extends React.Component { let disableContextMenu = false; if (element && isCustomElement(element)) { - const config = getCustomElementConfig( - this.props.customElementsConfig, - element.customType, - ); + const config = this.props.customElementsConfig?.[element.customType]; + disableContextMenu = !!config?.disableContextMenu; } if (disableContextMenu) { diff --git a/src/packages/excalidraw/example/App.js b/src/packages/excalidraw/example/App.js index 307ff288a..639afea04 100644 --- a/src/packages/excalidraw/example/App.js +++ b/src/packages/excalidraw/example/App.js @@ -205,8 +205,8 @@ export default function App() { }; const getCustomElementsConfig = () => { - return [ - { + return { + star: { type: "custom", customType: "star", displayData: { @@ -219,7 +219,7 @@ export default function App() { height: 60, disableContextMenu: true, }, - { + comment: { type: "custom", customType: "comment", displayData: { @@ -234,7 +234,7 @@ export default function App() { onCreate, disableContextMenu: true, }, - { + thumbsup: { type: "custom", customType: "thumbsup", displayData: { @@ -260,7 +260,7 @@ export default function App() { }, }, }, - ]; + }; }; const addTextArea = (element) => { diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index e5eae8c49..b5abb8ffc 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -52,11 +52,10 @@ const ExcalidrawBase = (props: ExcalidrawProps) => { ...canvasActions, }, }; - const customElementsConfig: AppProps["customElementsConfig"] = - props.customElementsConfig?.map((customElementConfig) => ({ - ...DEFAULT_CUSTOM_ELEMENT_CONFIG, - ...customElementConfig, - })); + const customElementsConfig = {} as AppProps["customElementsConfig"]; + Object.entries(props.customElementsConfig || {}).forEach(([key, value]) => { + customElementsConfig![key] = { ...DEFAULT_CUSTOM_ELEMENT_CONFIG, ...value }; + }); if (canvasActions?.export) { UIOptions.canvasActions.export.saveFileToDisk = diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index bdf11dc29..00d7404fe 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -25,13 +25,7 @@ import { RoughSVG } from "roughjs/bin/svg"; import { RoughGenerator } from "roughjs/bin/generator"; import { RenderConfig } from "../scene/types"; -import { - distance, - getFontString, - getFontFamilyString, - isRTL, - getCustomElementConfig, -} from "../utils"; +import { distance, getFontString, getFontFamilyString, isRTL } from "../utils"; import { isPathALoop } from "../math"; import rough from "roughjs/bin/rough"; import { AppState, BinaryFiles, Zoom } from "../types"; @@ -262,10 +256,8 @@ const drawElementOnCanvas = ( } case "custom": { - const config = getCustomElementConfig( - renderConfig.customElementsConfig, - element.customType, - ); + const config = renderConfig.customElementsConfig?.[element.customType]; + if (!config) { break; } diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 2c4fe9515..36c249a1a 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -2,7 +2,7 @@ import { RoughCanvas } from "roughjs/bin/canvas"; import { RoughSVG } from "roughjs/bin/svg"; import oc from "open-color"; -import { AppState, BinaryFiles, CustomElementConfig, Zoom } from "../types"; +import { AppState, BinaryFiles, Zoom } from "../types"; import { ExcalidrawElement, NonDeletedExcalidrawElement, @@ -47,11 +47,7 @@ import { TransformHandles, TransformHandleType, } from "../element/transformHandles"; -import { - viewportCoordsToSceneCoords, - supportsEmoji, - getCustomElementConfig, -} from "../utils"; +import { viewportCoordsToSceneCoords, supportsEmoji } from "../utils"; import { UserIdleState } from "../types"; import { THEME_FILTER } from "../constants"; import { @@ -309,16 +305,13 @@ export const renderScene = ( ) { const selections = elements.reduce((acc, element) => { const isCustom = element.type === "custom"; - let config: CustomElementConfig; + let config; const selectionColors = []; if (element.type === "custom") { - config = getCustomElementConfig( - renderConfig.customElementsConfig, - element.customType, - )!; + config = renderConfig.customElementsConfig?.[element.customType]; } - if (!isCustom || (isCustom && config!.transformHandles)) { + if (!isCustom || (isCustom && config && config.transformHandles)) { // local user if ( appState.selectedElementIds[element.id] && @@ -387,10 +380,11 @@ export const renderScene = ( if (locallySelectedElements.length === 1) { let showTransformHandles = true; if (locallySelectedElements[0].type === "custom") { - const config = getCustomElementConfig( - renderConfig.customElementsConfig, - locallySelectedElements[0].customType, - ); + const config = + renderConfig.customElementsConfig?.[ + locallySelectedElements[0].customType + ]; + if (!config || !config.transformHandles) { showTransformHandles = false; } diff --git a/src/scene/Scene.ts b/src/scene/Scene.ts index ba11d43ff..22f1625d3 100644 --- a/src/scene/Scene.ts +++ b/src/scene/Scene.ts @@ -7,7 +7,6 @@ import { getNonDeletedElements, isNonDeletedElement } from "../element"; import { LinearElementEditor } from "../element/linearElementEditor"; import App from "../components/App"; import { isCustomElement } from "../element/typeChecks"; -import { getCustomElementConfig } from "../utils"; type ElementIdKey = InstanceType["elementId"]; type ElementKey = ExcalidrawElement | ElementIdKey; @@ -104,10 +103,9 @@ class Scene { const elementsToBeStackedOnTop: ExcalidrawElement[] = []; nextElements.forEach((element) => { if (isCustomElement(element)) { - const config = getCustomElementConfig( - this.app.props.customElementsConfig, - element.customType, - ); + const config = + this.app.props.customElementsConfig?.[element.customType]; + if (config?.stackedOnTop) { elementsToBeStackedOnTop.push(element); } else { diff --git a/src/scene/selection.ts b/src/scene/selection.ts index a0667d2f9..7888f7cb7 100644 --- a/src/scene/selection.ts +++ b/src/scene/selection.ts @@ -5,7 +5,6 @@ import { import { getElementAbsoluteCoords, getElementBounds } from "../element"; import { AppProps, AppState } from "../types"; import { isBoundToContainer } from "../element/typeChecks"; -import { getCustomElementConfig } from "../utils"; export const getElementsWithinSelection = ( elements: readonly NonDeletedExcalidrawElement[], @@ -19,8 +18,7 @@ export const getElementsWithinSelection = ( getElementBounds(element); const isCustom = element.type === "custom"; const allowSelection = isCustom - ? getCustomElementConfig(customElementConfig, element.customType) - ?.transformHandles + ? customElementConfig?.[element.customType]?.transformHandles : true; return ( allowSelection && diff --git a/src/types.ts b/src/types.ts index f397af32a..ccce35439 100644 --- a/src/types.ts +++ b/src/types.ts @@ -303,7 +303,7 @@ export interface ExcalidrawProps { }>, ) => void; renderCustomElementWidget?: (appState: AppState) => void; - customElementsConfig?: CustomElementConfig[]; + customElementsConfig?: Record; onElementClick?: ( element: NonDeleted, event: React.PointerEvent, diff --git a/src/utils.ts b/src/utils.ts index e6a189ac7..fd20f7535 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,7 +11,7 @@ import { WINDOWS_EMOJI_FALLBACK_FONT, } from "./constants"; import { FontFamilyValues, FontString } from "./element/types"; -import { AppProps, AppState, DataURL, Zoom } from "./types"; +import { AppState, DataURL, Zoom } from "./types"; import { unstable_batchedUpdates } from "react-dom"; import { isDarwin } from "./keys"; @@ -629,16 +629,6 @@ export const getFrame = () => { } }; -export const getCustomElementConfig = ( - customElementConfig: AppProps["customElementsConfig"], - customType: string, -) => { - if (!customElementConfig) { - return null; - } - return customElementConfig.find((config) => config.customType === customType); -}; - export const isPromiseLike = ( value: any, ): value is Promise> => {