aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/element/typeChecks.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/excalidraw/element/typeChecks.ts')
-rw-r--r--packages/excalidraw/element/typeChecks.ts338
1 files changed, 338 insertions, 0 deletions
diff --git a/packages/excalidraw/element/typeChecks.ts b/packages/excalidraw/element/typeChecks.ts
new file mode 100644
index 0000000..6bb4269
--- /dev/null
+++ b/packages/excalidraw/element/typeChecks.ts
@@ -0,0 +1,338 @@
+import { ROUNDNESS } from "../constants";
+import type { ElementOrToolType } from "../types";
+import type { MarkNonNullable } from "../utility-types";
+import { assertNever } from "../utils";
+import type { Bounds } from "./bounds";
+import type {
+ ExcalidrawElement,
+ ExcalidrawTextElement,
+ ExcalidrawEmbeddableElement,
+ ExcalidrawLinearElement,
+ ExcalidrawBindableElement,
+ ExcalidrawFreeDrawElement,
+ InitializedExcalidrawImageElement,
+ ExcalidrawImageElement,
+ ExcalidrawTextElementWithContainer,
+ ExcalidrawTextContainer,
+ ExcalidrawFrameElement,
+ RoundnessType,
+ ExcalidrawFrameLikeElement,
+ ExcalidrawElementType,
+ ExcalidrawIframeElement,
+ ExcalidrawIframeLikeElement,
+ ExcalidrawMagicFrameElement,
+ ExcalidrawArrowElement,
+ ExcalidrawElbowArrowElement,
+ PointBinding,
+ FixedPointBinding,
+ ExcalidrawFlowchartNodeElement,
+} from "./types";
+
+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 isIframeElement = (
+ element: ExcalidrawElement | null,
+): element is ExcalidrawIframeElement => {
+ return !!element && element.type === "iframe";
+};
+
+export const isIframeLikeElement = (
+ element: ExcalidrawElement | null,
+): element is ExcalidrawIframeLikeElement => {
+ return (
+ !!element && (element.type === "iframe" || 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 isMagicFrameElement = (
+ element: ExcalidrawElement | null,
+): element is ExcalidrawMagicFrameElement => {
+ return element != null && element.type === "magicframe";
+};
+
+export const isFrameLikeElement = (
+ element: ExcalidrawElement | null,
+): element is ExcalidrawFrameLikeElement => {
+ return (
+ element != null &&
+ (element.type === "frame" || element.type === "magicframe")
+ );
+};
+
+export const isFreeDrawElement = (
+ element?: ExcalidrawElement | null,
+): element is ExcalidrawFreeDrawElement => {
+ return element != null && isFreeDrawElementType(element.type);
+};
+
+export const isFreeDrawElementType = (
+ elementType: ExcalidrawElementType,
+): 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 ExcalidrawArrowElement => {
+ return element != null && element.type === "arrow";
+};
+
+export const isElbowArrow = (
+ element?: ExcalidrawElement,
+): element is ExcalidrawElbowArrowElement => {
+ return isArrowElement(element) && element.elbowed;
+};
+
+export const isLinearElementType = (
+ elementType: ElementOrToolType,
+): 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: ElementOrToolType,
+): boolean => {
+ return elementType === "arrow";
+};
+
+export const isBindableElement = (
+ element: ExcalidrawElement | null | undefined,
+ 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 === "iframe" ||
+ element.type === "embeddable" ||
+ element.type === "frame" ||
+ element.type === "magicframe" ||
+ (element.type === "text" && !element.containerId))
+ );
+};
+
+export const isRectanguloidElement = (
+ element?: ExcalidrawElement | null,
+): element is ExcalidrawBindableElement => {
+ return (
+ element != null &&
+ (element.type === "rectangle" ||
+ element.type === "diamond" ||
+ element.type === "image" ||
+ element.type === "iframe" ||
+ element.type === "embeddable" ||
+ element.type === "frame" ||
+ element.type === "magicframe" ||
+ (element.type === "text" && !element.containerId))
+ );
+};
+
+// TODO: Remove this when proper distance calculation is introduced
+// @see binding.ts:distanceToBindableElement()
+export const isRectangularElement = (
+ element?: ExcalidrawElement | null,
+): element is ExcalidrawBindableElement => {
+ return (
+ element != null &&
+ (element.type === "rectangle" ||
+ element.type === "image" ||
+ element.type === "text" ||
+ element.type === "iframe" ||
+ element.type === "embeddable" ||
+ element.type === "frame" ||
+ element.type === "magicframe" ||
+ element.type === "freedraw")
+ );
+};
+
+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: ExcalidrawElementType | undefined = element?.type;
+ if (!type) {
+ return false;
+ }
+ switch (type) {
+ case "text":
+ case "diamond":
+ case "rectangle":
+ case "iframe":
+ case "embeddable":
+ case "ellipse":
+ case "arrow":
+ case "freedraw":
+ case "line":
+ case "frame":
+ case "magicframe":
+ case "image":
+ case "selection": {
+ return true;
+ }
+ default: {
+ assertNever(type, null);
+ return false;
+ }
+ }
+};
+
+export const isFlowchartNodeElement = (
+ element: ExcalidrawElement,
+): element is ExcalidrawFlowchartNodeElement => {
+ return (
+ element.type === "rectangle" ||
+ element.type === "ellipse" ||
+ element.type === "diamond"
+ );
+};
+
+export const hasBoundTextElement = (
+ element: ExcalidrawElement | null,
+): element is MarkNonNullable<ExcalidrawBindableElement, "boundElements"> => {
+ 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" ||
+ type === "iframe" ||
+ type === "image";
+
+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;
+};
+
+export const isFixedPointBinding = (
+ binding: PointBinding | FixedPointBinding,
+): binding is FixedPointBinding => {
+ return (
+ Object.hasOwn(binding, "fixedPoint") &&
+ (binding as FixedPointBinding).fixedPoint != null
+ );
+};
+
+// TODO: Move this to @excalidraw/math
+export const isBounds = (box: unknown): box is Bounds =>
+ Array.isArray(box) &&
+ box.length === 4 &&
+ typeof box[0] === "number" &&
+ typeof box[1] === "number" &&
+ typeof box[2] === "number" &&
+ typeof box[3] === "number";