import { ROUNDNESS } from "../constants"; import { AppState } from "../types"; import { MarkNonNullable } from "../utility-types"; import { assertNever } from "../utils"; import { ExcalidrawElement, ExcalidrawTextElement, ExcalidrawEmbeddableElement, ExcalidrawLinearElement, ExcalidrawBindableElement, ExcalidrawGenericElement, ExcalidrawFreeDrawElement, InitializedExcalidrawImageElement, ExcalidrawImageElement, ExcalidrawTextElementWithContainer, ExcalidrawTextContainer, ExcalidrawFrameElement, RoundnessType, } from "./types"; export const isGenericElement = ( element: ExcalidrawElement | null, ): element is ExcalidrawGenericElement => { return ( element != null && (element.type === "selection" || element.type === "rectangle" || element.type === "diamond" || element.type === "ellipse" || element.type === "embeddable") ); }; export const isInitializedImageElement = ( element: ExcalidrawElement | null, ): element is InitializedExcalidrawImageElement => { return !!element && element.type === "image" && !!element.fileId; }; export const isImageElement = ( element: ExcalidrawElement | null, ): element is ExcalidrawImageElement => { return !!element && element.type === "image"; }; export const isEmbeddableElement = ( element: ExcalidrawElement | null | undefined, ): element is ExcalidrawEmbeddableElement => { return !!element && element.type === "embeddable"; }; export const isTextElement = ( element: ExcalidrawElement | null, ): element is ExcalidrawTextElement => { return element != null && element.type === "text"; }; export const isFrameElement = ( element: ExcalidrawElement | null, ): element is ExcalidrawFrameElement => { return element != null && element.type === "frame"; }; export const isFreeDrawElement = ( element?: ExcalidrawElement | null, ): element is ExcalidrawFreeDrawElement => { return element != null && isFreeDrawElementType(element.type); }; export const isFreeDrawElementType = ( elementType: ExcalidrawElement["type"], ): boolean => { return elementType === "freedraw"; }; export const isLinearElement = ( element?: ExcalidrawElement | null, ): element is ExcalidrawLinearElement => { return element != null && isLinearElementType(element.type); }; export const isArrowElement = ( element?: ExcalidrawElement | null, ): element is ExcalidrawLinearElement => { return element != null && element.type === "arrow"; }; export const isLinearElementType = ( elementType: AppState["activeTool"]["type"], ): boolean => { return ( elementType === "arrow" || elementType === "line" // || elementType === "freedraw" ); }; export const isBindingElement = ( element?: ExcalidrawElement | null, includeLocked = true, ): element is ExcalidrawLinearElement => { return ( element != null && (!element.locked || includeLocked === true) && isBindingElementType(element.type) ); }; export const isBindingElementType = ( elementType: AppState["activeTool"]["type"], ): boolean => { return elementType === "arrow"; }; export const isBindableElement = ( element: ExcalidrawElement | null, includeLocked = true, ): element is ExcalidrawBindableElement => { return ( element != null && (!element.locked || includeLocked === true) && (element.type === "rectangle" || element.type === "diamond" || element.type === "ellipse" || element.type === "image" || element.type === "embeddable" || element.type === "frame" || (element.type === "text" && !element.containerId)) ); }; export const isTextBindableContainer = ( element: ExcalidrawElement | null, includeLocked = true, ): element is ExcalidrawTextContainer => { return ( element != null && (!element.locked || includeLocked === true) && (element.type === "rectangle" || element.type === "diamond" || element.type === "ellipse" || isArrowElement(element)) ); }; export const isExcalidrawElement = ( element: any, ): element is ExcalidrawElement => { const type: ExcalidrawElement["type"] | undefined = element?.type; if (!type) { return false; } switch (type) { case "text": case "diamond": case "rectangle": case "embeddable": case "ellipse": case "arrow": case "freedraw": case "line": case "frame": case "image": case "selection": { return true; } default: { assertNever(type, null); return false; } } }; export const hasBoundTextElement = ( element: ExcalidrawElement | null, ): element is MarkNonNullable => { return ( isTextBindableContainer(element) && !!element.boundElements?.some(({ type }) => type === "text") ); }; export const isBoundToContainer = ( element: ExcalidrawElement | null, ): element is ExcalidrawTextElementWithContainer => { return ( element !== null && "containerId" in element && element.containerId !== null && isTextElement(element) ); }; export const isUsingAdaptiveRadius = (type: string) => type === "rectangle" || type === "embeddable"; export const isUsingProportionalRadius = (type: string) => type === "line" || type === "arrow" || type === "diamond"; export const canApplyRoundnessTypeToElement = ( roundnessType: RoundnessType, element: ExcalidrawElement, ) => { if ( (roundnessType === ROUNDNESS.ADAPTIVE_RADIUS || // if legacy roundness, it can be applied to elements that currently // use adaptive radius roundnessType === ROUNDNESS.LEGACY) && isUsingAdaptiveRadius(element.type) ) { return true; } if ( roundnessType === ROUNDNESS.PROPORTIONAL_RADIUS && isUsingProportionalRadius(element.type) ) { return true; } return false; }; export const getDefaultRoundnessTypeForElement = ( element: ExcalidrawElement, ) => { if (isUsingProportionalRadius(element.type)) { return { type: ROUNDNESS.PROPORTIONAL_RADIUS, }; } if (isUsingAdaptiveRadius(element.type)) { return { type: ROUNDNESS.ADAPTIVE_RADIUS, }; } return null; };