refactor: separate elements logic into a standalone package (#9285)
Some checks failed
Auto release excalidraw next / Auto-release-excalidraw-next (push) Failing after 2m36s
Build Docker image / build-docker (push) Failing after 6s
Cancel previous runs / cancel (push) Failing after 1s
Publish Docker / publish-docker (push) Failing after 31s
New Sentry production release / sentry (push) Failing after 2m3s

This commit is contained in:
Marcel Mraz 2025-03-26 15:24:59 +01:00 committed by GitHub
parent a18f059188
commit 432a46ef9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
372 changed files with 3466 additions and 2466 deletions

View file

@ -1,3 +1,10 @@
import clsx from "clsx";
import throttle from "lodash.throttle";
import React, { useContext } from "react";
import { flushSync } from "react-dom";
import rough from "roughjs/bin/rough";
import { nanoid } from "nanoid";
import {
clamp,
pointFrom,
@ -11,16 +18,318 @@ import {
vectorNormalize,
} from "@excalidraw/math";
import { isPointInShape } from "@excalidraw/utils/collision";
import { getSelectionBoxShape } from "@excalidraw/utils/geometry/shape";
import clsx from "clsx";
import throttle from "lodash.throttle";
import { nanoid } from "nanoid";
import React, { useContext } from "react";
import { flushSync } from "react-dom";
import rough from "roughjs/bin/rough";
import { getSelectionBoxShape } from "@excalidraw/utils/shape";
import {
COLOR_PALETTE,
CODES,
shouldResizeFromCenter,
shouldMaintainAspectRatio,
shouldRotateWithDiscreteAngle,
isArrowKey,
KEYS,
APP_NAME,
CURSOR_TYPE,
DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT,
DEFAULT_VERTICAL_ALIGN,
DRAGGING_THRESHOLD,
ELEMENT_SHIFT_TRANSLATE_AMOUNT,
ELEMENT_TRANSLATE_AMOUNT,
EVENT,
FRAME_STYLE,
IMAGE_MIME_TYPES,
IMAGE_RENDER_TIMEOUT,
isBrave,
LINE_CONFIRM_THRESHOLD,
MAX_ALLOWED_FILE_BYTES,
MIME_TYPES,
MQ_MAX_HEIGHT_LANDSCAPE,
MQ_MAX_WIDTH_LANDSCAPE,
MQ_MAX_WIDTH_PORTRAIT,
MQ_RIGHT_SIDEBAR_MIN_WIDTH,
POINTER_BUTTON,
ROUNDNESS,
SCROLL_TIMEOUT,
TAP_TWICE_TIMEOUT,
TEXT_TO_CENTER_SNAP_THRESHOLD,
THEME,
THEME_FILTER,
TOUCH_CTX_MENU_TIMEOUT,
VERTICAL_ALIGN,
YOUTUBE_STATES,
ZOOM_STEP,
POINTER_EVENTS,
TOOL_TYPE,
isIOS,
supportsResizeObserver,
DEFAULT_COLLISION_THRESHOLD,
DEFAULT_TEXT_ALIGN,
ARROW_TYPE,
DEFAULT_REDUCED_GLOBAL_ALPHA,
isSafari,
isLocalLink,
normalizeLink,
toValidURL,
getGridPoint,
getLineHeight,
debounce,
distance,
getFontString,
getNearestScrollableContainer,
isInputLike,
isToolIcon,
isWritableElement,
sceneCoordsToViewportCoords,
tupleToCoors,
viewportCoordsToSceneCoords,
wrapEvent,
updateObject,
updateActiveTool,
getShortcutKey,
isTransparent,
easeToValuesRAF,
muteFSAbortError,
isTestEnv,
isDevEnv,
easeOut,
updateStable,
addEventListener,
normalizeEOL,
getDateTime,
isShallowEqual,
arrayToMap,
type EXPORT_IMAGE_TYPES,
} from "@excalidraw/common";
import {
getCommonBounds,
getElementAbsoluteCoords,
} from "@excalidraw/element/bounds";
import {
bindOrUnbindLinearElement,
bindOrUnbindLinearElements,
fixBindingsAfterDeletion,
getHoveredElementForBinding,
isBindingEnabled,
isLinearElementSimpleAndAlreadyBound,
maybeBindLinearElement,
shouldEnableBindingForPointerEvent,
updateBoundElements,
getSuggestedBindingsForArrows,
} from "@excalidraw/element/binding";
import { LinearElementEditor } from "@excalidraw/element/linearElementEditor";
import {
mutateElement,
newElementWith,
} from "@excalidraw/element/mutateElement";
import {
newFrameElement,
newFreeDrawElement,
newEmbeddableElement,
newMagicFrameElement,
newIframeElement,
newArrowElement,
newElement,
newImageElement,
newLinearElement,
newTextElement,
refreshTextDimensions,
} from "@excalidraw/element/newElement";
import {
deepCopyElement,
duplicateElements,
} from "@excalidraw/element/duplicate";
import {
hasBoundTextElement,
isArrowElement,
isBindingElement,
isBindingElementType,
isBoundToContainer,
isFrameLikeElement,
isImageElement,
isEmbeddableElement,
isInitializedImageElement,
isLinearElement,
isLinearElementType,
isUsingAdaptiveRadius,
isIframeElement,
isIframeLikeElement,
isMagicFrameElement,
isTextBindableContainer,
isElbowArrow,
isFlowchartNodeElement,
isBindableElement,
isTextElement,
} from "@excalidraw/element/typeChecks";
import {
getLockedLinearCursorAlignSize,
getNormalizedDimensions,
isElementCompletelyInViewport,
isElementInViewport,
isInvisiblySmallElement,
} from "@excalidraw/element/sizeHelpers";
import {
getBoundTextShape,
getCornerRadius,
getElementShape,
isPathALoop,
} from "@excalidraw/element/shapes";
import {
createSrcDoc,
embeddableURLValidator,
maybeParseEmbedSrc,
getEmbedLink,
} from "@excalidraw/element/embeddable";
import {
getInitializedImageElements,
loadHTMLImageElement,
normalizeSVG,
updateImageCache as _updateImageCache,
} from "@excalidraw/element/image";
import {
getBoundTextElement,
getContainerCenter,
getContainerElement,
isValidTextContainer,
redrawTextBoundingBox,
} from "@excalidraw/element/textElement";
import { shouldShowBoundingBox } from "@excalidraw/element/transformHandles";
import {
getFrameChildren,
isCursorInFrame,
addElementsToFrame,
replaceAllElementsInFrame,
removeElementsFromFrame,
getElementsInResizingFrame,
getElementsInNewFrame,
getContainingFrame,
elementOverlapsWithFrame,
updateFrameMembershipOfSelectedElements,
isElementInFrame,
getFrameLikeTitle,
getElementsOverlappingFrame,
filterElementsEligibleAsFrameChildren,
} from "@excalidraw/element/frame";
import {
hitElementBoundText,
hitElementBoundingBoxOnly,
hitElementItself,
} from "@excalidraw/element/collision";
import { getVisibleSceneBounds } from "@excalidraw/element/bounds";
import {
FlowChartCreator,
FlowChartNavigator,
getLinkDirectionFromKey,
} from "@excalidraw/element/flowchart";
import { cropElement } from "@excalidraw/element/cropElement";
import { wrapText } from "@excalidraw/element/textWrapping";
import {
isElementLink,
parseElementLinkFromURL,
} from "@excalidraw/element/elementLink";
import {
isMeasureTextSupported,
normalizeText,
measureText,
getLineHeightInPx,
getApproxMinLineWidth,
getApproxMinLineHeight,
getMinTextElementWidth,
} from "@excalidraw/element/textMeasurements";
import { ShapeCache } from "@excalidraw/element/ShapeCache";
import { getRenderOpacity } from "@excalidraw/element/renderElement";
import {
editGroupForSelectedElement,
getElementsInGroup,
getSelectedGroupIdForElement,
getSelectedGroupIds,
isElementInGroup,
isSelectedViaGroup,
selectGroupsForSelectedElements,
} from "@excalidraw/element/groups";
import {
syncInvalidIndices,
syncMovedIndices,
} from "@excalidraw/element/fractionalIndex";
import {
excludeElementsInFramesFromSelection,
makeNextSelectedElementIds,
} from "@excalidraw/element/selection";
import {
getResizeOffsetXY,
getResizeArrowDirection,
transformElements,
} from "@excalidraw/element/resizeElements";
import {
getCursorForResizingElement,
getElementWithTransformHandleType,
getTransformHandleTypeFromCoords,
} from "@excalidraw/element/resizeTest";
import {
dragNewElement,
dragSelectedElements,
getDragOffsetXY,
} from "@excalidraw/element/dragElements";
import { isNonDeletedElement } from "@excalidraw/element";
import type { LocalPoint, Radians } from "@excalidraw/math";
import type {
ExcalidrawBindableElement,
ExcalidrawElement,
ExcalidrawFreeDrawElement,
ExcalidrawGenericElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
NonDeleted,
InitializedExcalidrawImageElement,
ExcalidrawImageElement,
FileId,
NonDeletedExcalidrawElement,
ExcalidrawTextContainer,
ExcalidrawFrameLikeElement,
ExcalidrawMagicFrameElement,
ExcalidrawIframeLikeElement,
IframeData,
ExcalidrawIframeElement,
ExcalidrawEmbeddableElement,
Ordered,
MagicGenerationData,
ExcalidrawNonSelectionElement,
ExcalidrawArrowElement,
} from "@excalidraw/element/types";
import type { ValueOf } from "@excalidraw/common/utility-types";
import {
actionAddToLibrary,
actionBringForward,
@ -77,143 +386,13 @@ import {
isHandToolActive,
} from "../appState";
import { copyTextToSystemClipboard, parseClipboard } from "../clipboard";
import {
APP_NAME,
CURSOR_TYPE,
DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT,
DEFAULT_VERTICAL_ALIGN,
DRAGGING_THRESHOLD,
ELEMENT_SHIFT_TRANSLATE_AMOUNT,
ELEMENT_TRANSLATE_AMOUNT,
EVENT,
FRAME_STYLE,
IMAGE_MIME_TYPES,
IMAGE_RENDER_TIMEOUT,
isBrave,
LINE_CONFIRM_THRESHOLD,
MAX_ALLOWED_FILE_BYTES,
MIME_TYPES,
MQ_MAX_HEIGHT_LANDSCAPE,
MQ_MAX_WIDTH_LANDSCAPE,
MQ_MAX_WIDTH_PORTRAIT,
MQ_RIGHT_SIDEBAR_MIN_WIDTH,
POINTER_BUTTON,
ROUNDNESS,
SCROLL_TIMEOUT,
TAP_TWICE_TIMEOUT,
TEXT_TO_CENTER_SNAP_THRESHOLD,
THEME,
THEME_FILTER,
TOUCH_CTX_MENU_TIMEOUT,
VERTICAL_ALIGN,
YOUTUBE_STATES,
ZOOM_STEP,
POINTER_EVENTS,
TOOL_TYPE,
isIOS,
supportsResizeObserver,
DEFAULT_COLLISION_THRESHOLD,
DEFAULT_TEXT_ALIGN,
ARROW_TYPE,
DEFAULT_REDUCED_GLOBAL_ALPHA,
isSafari,
type EXPORT_IMAGE_TYPES,
} from "../constants";
import { exportCanvas, loadFromBlob } from "../data";
import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
import { restore, restoreElements } from "../data/restore";
import {
dragNewElement,
dragSelectedElements,
getCommonBounds,
getCursorForResizingElement,
getDragOffsetXY,
getElementWithTransformHandleType,
getNormalizedDimensions,
getResizeArrowDirection,
getResizeOffsetXY,
getLockedLinearCursorAlignSize,
getTransformHandleTypeFromCoords,
isInvisiblySmallElement,
isNonDeletedElement,
isTextElement,
newElement,
newLinearElement,
newTextElement,
newImageElement,
transformElements,
refreshTextDimensions,
redrawTextBoundingBox,
getElementAbsoluteCoords,
} from "../element";
import {
bindOrUnbindLinearElement,
bindOrUnbindLinearElements,
fixBindingsAfterDeletion,
getHoveredElementForBinding,
isBindingEnabled,
isLinearElementSimpleAndAlreadyBound,
maybeBindLinearElement,
shouldEnableBindingForPointerEvent,
updateBoundElements,
getSuggestedBindingsForArrows,
} from "../element/binding";
import { LinearElementEditor } from "../element/linearElementEditor";
import { mutateElement, newElementWith } from "../element/mutateElement";
import { deepCopyElement, duplicateElements } from "../element/duplicate";
import {
newFrameElement,
newFreeDrawElement,
newEmbeddableElement,
newMagicFrameElement,
newIframeElement,
newArrowElement,
} from "../element/newElement";
import {
hasBoundTextElement,
isArrowElement,
isBindingElement,
isBindingElementType,
isBoundToContainer,
isFrameLikeElement,
isImageElement,
isEmbeddableElement,
isInitializedImageElement,
isLinearElement,
isLinearElementType,
isUsingAdaptiveRadius,
isIframeElement,
isIframeLikeElement,
isMagicFrameElement,
isTextBindableContainer,
isElbowArrow,
isFlowchartNodeElement,
isBindableElement,
} from "../element/typeChecks";
import { getCenter, getDistance } from "../gesture";
import {
editGroupForSelectedElement,
getElementsInGroup,
getSelectedGroupIdForElement,
getSelectedGroupIds,
isElementInGroup,
isSelectedViaGroup,
selectGroupsForSelectedElements,
} from "../groups";
import { History } from "../history";
import { defaultLang, getLanguage, languages, setLanguage, t } from "../i18n";
import {
CODES,
shouldResizeFromCenter,
shouldMaintainAspectRatio,
shouldRotateWithDiscreteAngle,
isArrowKey,
KEYS,
} from "../keys";
import {
isElementCompletelyInViewport,
isElementInViewport,
} from "../element/sizeHelpers";
import {
calculateScrollCenter,
getElementsWithinSelection,
@ -224,47 +403,6 @@ import {
} from "../scene";
import Scene from "../scene/Scene";
import { getStateForZoom } from "../scene/zoom";
import {
findShapeByKey,
getBoundTextShape,
getCornerRadius,
getElementShape,
isPathALoop,
} from "../shapes";
import {
debounce,
distance,
getFontString,
getNearestScrollableContainer,
isInputLike,
isToolIcon,
isWritableElement,
sceneCoordsToViewportCoords,
tupleToCoors,
viewportCoordsToSceneCoords,
wrapEvent,
updateObject,
updateActiveTool,
getShortcutKey,
isTransparent,
easeToValuesRAF,
muteFSAbortError,
isTestEnv,
easeOut,
updateStable,
addEventListener,
normalizeEOL,
getDateTime,
isShallowEqual,
arrayToMap,
isDevEnv,
} from "../utils";
import {
createSrcDoc,
embeddableURLValidator,
maybeParseEmbedSrc,
getEmbedLink,
} from "../element/embeddable";
import {
dataURLToFile,
dataURLToString,
@ -281,47 +419,15 @@ import {
resizeImageFile,
SVGStringToFile,
} from "../data/blob";
import {
getInitializedImageElements,
loadHTMLImageElement,
normalizeSVG,
updateImageCache as _updateImageCache,
} from "../element/image";
import { fileOpen } from "../data/filesystem";
import {
getBoundTextElement,
getContainerCenter,
getContainerElement,
isValidTextContainer,
} from "../element/textElement";
import {
showHyperlinkTooltip,
hideHyperlinkToolip,
Hyperlink,
} from "../components/hyperlink/Hyperlink";
import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
import { shouldShowBoundingBox } from "../element/transformHandles";
import { Fonts, getLineHeight } from "../fonts";
import {
getFrameChildren,
isCursorInFrame,
addElementsToFrame,
replaceAllElementsInFrame,
removeElementsFromFrame,
getElementsInResizingFrame,
getElementsInNewFrame,
getContainingFrame,
elementOverlapsWithFrame,
updateFrameMembershipOfSelectedElements,
isElementInFrame,
getFrameLikeTitle,
getElementsOverlappingFrame,
filterElementsEligibleAsFrameChildren,
} from "../frame";
import {
excludeElementsInFramesFromSelection,
makeNextSelectedElementIds,
} from "../scene/selection";
import { Fonts } from "../fonts";
import { editorJotaiStore } from "../editor-jotai";
import { ImageSceneDataError } from "../errors";
import {
@ -335,11 +441,9 @@ import {
getReferenceSnapPoints,
SnapCache,
isGridModeEnabled,
getGridPoint,
} from "../snapping";
import { convertToExcalidrawElements } from "../data/transform";
import { Renderer } from "../scene/Renderer";
import { ShapeCache } from "../scene/ShapeCache";
import {
setEraserCursor,
setCursor,
@ -347,40 +451,15 @@ import {
setCursorForShape,
} from "../cursor";
import { Emitter } from "../emitter";
import { ElementCanvasButtons } from "../element/ElementCanvasButtons";
import { COLOR_PALETTE } from "../colors";
import { ElementCanvasButtons } from "../components/ElementCanvasButtons";
import { Store, CaptureUpdateAction } from "../store";
import { AnimatedTrail } from "../animated-trail";
import { LaserTrails } from "../laser-trails";
import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
import { getRenderOpacity } from "../renderer/renderElement";
import {
hitElementBoundText,
hitElementBoundingBoxOnly,
hitElementItself,
} from "../element/collision";
import { textWysiwyg } from "../element/textWysiwyg";
import { textWysiwyg } from "../wysiwyg/textWysiwyg";
import { isOverScrollBars } from "../scene/scrollbars";
import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
import { getVisibleSceneBounds } from "../element/bounds";
import { isMaybeMermaidDefinition } from "../mermaid";
import {
FlowChartCreator,
FlowChartNavigator,
getLinkDirectionFromKey,
} from "../element/flowchart";
import { cropElement } from "../element/cropElement";
import { wrapText } from "../element/textWrapping";
import { isElementLink, parseElementLinkFromURL } from "../element/elementLink";
import {
isMeasureTextSupported,
normalizeText,
measureText,
getLineHeightInPx,
getApproxMinLineWidth,
getApproxMinLineHeight,
getMinTextElementWidth,
} from "../element/textMeasurements";
import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
import BraveMeasureTextError from "./BraveMeasureTextError";
@ -401,40 +480,19 @@ import {
import { MagicIcon, copyIcon, fullscreenIcon } from "./icons";
import { Toast } from "./Toast";
import type { Action, ActionResult } from "../actions/types";
import { findShapeByKey } from "./shapes";
import type {
RenderInteractiveSceneCallback,
ScrollBars,
} from "../scene/types";
import type { PastedMixedContent } from "../clipboard";
import type { ExportedElements } from "../data";
import type { ContextMenuItems } from "./ContextMenu";
import type { FileSystemHandle } from "../data/filesystem";
import type { ExcalidrawElementSkeleton } from "../data/transform";
import type {
ExcalidrawBindableElement,
ExcalidrawElement,
ExcalidrawFreeDrawElement,
ExcalidrawGenericElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
NonDeleted,
InitializedExcalidrawImageElement,
ExcalidrawImageElement,
FileId,
NonDeletedExcalidrawElement,
ExcalidrawTextContainer,
ExcalidrawFrameLikeElement,
ExcalidrawMagicFrameElement,
ExcalidrawIframeLikeElement,
IframeData,
ExcalidrawIframeElement,
ExcalidrawEmbeddableElement,
Ordered,
MagicGenerationData,
ExcalidrawNonSelectionElement,
ExcalidrawArrowElement,
} from "../element/types";
import type {
RenderInteractiveSceneCallback,
ScrollBars,
} from "../scene/types";
import type {
AppClassProperties,
AppProps,
@ -463,8 +521,8 @@ import type {
NullableGridSize,
Offsets,
} from "../types";
import type { ValueOf } from "../utility-types";
import type { RoughCanvas } from "roughjs/bin/canvas";
import type { Action, ActionResult } from "../actions/types";
const AppContext = React.createContext<AppClassProperties>(null!);
const AppPropsContext = React.createContext<AppProps>(null!);