@excalidraw/excalidraw 0.17.1-3e334a6 → 0.17.1-4689a6b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-M7HSOQ7X.js → chunk-23CKV3WP.js} +3 -1
- package/dist/browser/dev/excalidraw-assets-dev/chunk-23CKV3WP.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-RWZVJAQU.js → chunk-7D5BMEAB.js} +2227 -1976
- package/dist/browser/dev/excalidraw-assets-dev/chunk-7D5BMEAB.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-R45KN4KN.js → en-W7TECCRB.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js → image-JKT6GXZD.js} +2 -2
- package/dist/browser/dev/index.css +20 -0
- package/dist/browser/dev/index.css.map +2 -2
- package/dist/browser/dev/index.js +766 -580
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/chunk-DWOM5R6H.js +55 -0
- package/dist/browser/prod/excalidraw-assets/{chunk-DIHRGRYX.js → chunk-SK23VHAR.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/{en-H6IY7PV6.js → en-SMMH575S.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/image-WDEQS5RL.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +22 -22
- package/dist/{prod/en-N2RZZLK5.json → dev/en-CVBEBUBY.json} +2 -0
- package/dist/dev/index.css +20 -0
- package/dist/dev/index.css.map +2 -2
- package/dist/dev/index.js +2379 -2069
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionBoundText.js +4 -1
- package/dist/excalidraw/actions/actionCanvas.js +3 -1
- package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -0
- package/dist/excalidraw/actions/actionExport.d.ts +1 -1
- package/dist/excalidraw/actions/actionFinalize.d.ts +1 -1
- package/dist/excalidraw/actions/actionFinalize.js +3 -3
- package/dist/excalidraw/actions/actionFlip.d.ts +3 -3
- package/dist/excalidraw/actions/actionFlip.js +6 -6
- package/dist/excalidraw/actions/actionGroup.js +4 -2
- package/dist/excalidraw/actions/actionHistory.js +3 -0
- package/dist/excalidraw/actions/actionZindex.d.ts +11 -11
- package/dist/excalidraw/analytics.js +1 -1
- package/dist/excalidraw/components/App.d.ts +13 -3
- package/dist/excalidraw/components/App.js +211 -81
- package/dist/excalidraw/components/CommandPalette/CommandPalette.js +24 -10
- package/dist/excalidraw/components/DarkModeToggle.js +3 -1
- package/dist/excalidraw/components/HelpDialog.js +2 -2
- package/dist/excalidraw/components/RadioGroup.d.ts +2 -1
- package/dist/excalidraw/components/RadioGroup.js +1 -1
- package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.js +6 -2
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +18 -0
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.js +9 -0
- package/dist/excalidraw/components/hyperlink/Hyperlink.js +3 -3
- package/dist/excalidraw/components/hyperlink/helpers.js +2 -3
- package/dist/excalidraw/components/icons.d.ts +3 -0
- package/dist/excalidraw/components/icons.js +5 -1
- package/dist/excalidraw/components/main-menu/DefaultItems.d.ts +12 -2
- package/dist/excalidraw/components/main-menu/DefaultItems.js +38 -7
- package/dist/excalidraw/constants.d.ts +0 -3
- package/dist/excalidraw/constants.js +0 -3
- package/dist/excalidraw/data/magic.js +2 -1
- package/dist/excalidraw/data/reconcile.d.ts +6 -0
- package/dist/excalidraw/data/reconcile.js +49 -0
- package/dist/excalidraw/data/restore.d.ts +3 -3
- package/dist/excalidraw/data/restore.js +5 -6
- package/dist/excalidraw/data/transform.d.ts +1 -1
- package/dist/excalidraw/data/transform.js +12 -3
- package/dist/excalidraw/element/binding.d.ts +22 -9
- package/dist/excalidraw/element/binding.js +403 -26
- package/dist/excalidraw/element/bounds.d.ts +0 -1
- package/dist/excalidraw/element/bounds.js +0 -3
- package/dist/excalidraw/element/collision.d.ts +14 -19
- package/dist/excalidraw/element/collision.js +36 -713
- package/dist/excalidraw/element/embeddable.js +18 -43
- package/dist/excalidraw/element/index.d.ts +0 -1
- package/dist/excalidraw/element/index.js +0 -1
- package/dist/excalidraw/element/linearElementEditor.d.ts +10 -10
- package/dist/excalidraw/element/linearElementEditor.js +6 -4
- package/dist/excalidraw/element/newElement.d.ts +1 -1
- package/dist/excalidraw/element/newElement.js +2 -1
- package/dist/excalidraw/element/textElement.d.ts +0 -1
- package/dist/excalidraw/element/textElement.js +0 -30
- package/dist/excalidraw/element/types.d.ts +17 -2
- package/dist/excalidraw/errors.d.ts +3 -0
- package/dist/excalidraw/errors.js +3 -0
- package/dist/excalidraw/fractionalIndex.d.ts +40 -0
- package/dist/excalidraw/fractionalIndex.js +241 -0
- package/dist/excalidraw/frame.d.ts +1 -1
- package/dist/excalidraw/hooks/useCreatePortalContainer.js +2 -1
- package/dist/excalidraw/locales/en.json +2 -0
- package/dist/excalidraw/renderer/helpers.js +2 -2
- package/dist/excalidraw/renderer/interactiveScene.js +1 -1
- package/dist/excalidraw/renderer/renderElement.js +3 -3
- package/dist/excalidraw/renderer/renderSnaps.js +2 -1
- package/dist/excalidraw/scene/Scene.d.ts +7 -6
- package/dist/excalidraw/scene/Scene.js +28 -13
- package/dist/excalidraw/scene/export.js +4 -3
- package/dist/excalidraw/types.d.ts +4 -3
- package/dist/excalidraw/utils.d.ts +1 -0
- package/dist/excalidraw/utils.js +1 -0
- package/dist/excalidraw/zindex.d.ts +2 -2
- package/dist/excalidraw/zindex.js +9 -13
- package/dist/{dev/en-N2RZZLK5.json → prod/en-CVBEBUBY.json} +2 -0
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +36 -36
- package/dist/utils/collision.d.ts +4 -0
- package/dist/utils/collision.js +48 -0
- package/dist/utils/geometry/geometry.d.ts +71 -0
- package/dist/utils/geometry/geometry.js +674 -0
- package/dist/utils/geometry/shape.d.ts +55 -0
- package/dist/utils/geometry/shape.js +149 -0
- package/package.json +2 -1
- package/dist/browser/dev/excalidraw-assets-dev/chunk-M7HSOQ7X.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-RWZVJAQU.js.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-LL4GORAM.js +0 -55
- package/dist/browser/prod/excalidraw-assets/image-EFCJDJH3.js +0 -1
- /package/dist/browser/dev/excalidraw-assets-dev/{en-R45KN4KN.js.map → en-W7TECCRB.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js.map → image-JKT6GXZD.js.map} +0 -0
|
@@ -15,12 +15,12 @@ import { APP_NAME, CURSOR_TYPE, DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT, DEFAULT_VERTI
|
|
|
15
15
|
import { exportCanvas, loadFromBlob } from "../data";
|
|
16
16
|
import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
|
|
17
17
|
import { restore, restoreElements } from "../data/restore";
|
|
18
|
-
import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords,
|
|
18
|
+
import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, updateTextElement, redrawTextBoundingBox, getElementAbsoluteCoords, } from "../element";
|
|
19
19
|
import { bindOrUnbindLinearElement, bindOrUnbindSelectedElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getEligibleElementsForBinding, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, unbindLinearElements, updateBoundElements, } from "../element/binding";
|
|
20
20
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
21
21
|
import { mutateElement, newElementWith } from "../element/mutateElement";
|
|
22
22
|
import { deepCopyElement, duplicateElements, newFrameElement, newFreeDrawElement, newEmbeddableElement, newMagicFrameElement, newIframeElement, } from "../element/newElement";
|
|
23
|
-
import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isFrameElement, isIframeElement, isIframeLikeElement, isMagicFrameElement, } from "../element/typeChecks";
|
|
23
|
+
import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isFrameElement, isIframeElement, isIframeLikeElement, isMagicFrameElement, isTextBindableContainer, } from "../element/typeChecks";
|
|
24
24
|
import { getCenter, getDistance } from "../gesture";
|
|
25
25
|
import { editGroupForSelectedElement, getElementsInGroup, getSelectedGroupIdForElement, getSelectedGroupIds, isElementInGroup, isSelectedViaGroup, selectGroupsForSelectedElements, } from "../groups";
|
|
26
26
|
import History from "../history";
|
|
@@ -28,11 +28,13 @@ import { defaultLang, getLanguage, languages, setLanguage, t } from "../i18n";
|
|
|
28
28
|
import { CODES, shouldResizeFromCenter, shouldMaintainAspectRatio, shouldRotateWithDiscreteAngle, isArrowKey, KEYS, } from "../keys";
|
|
29
29
|
import { isElementInViewport } from "../element/sizeHelpers";
|
|
30
30
|
import { distance2d, getCornerRadius, getGridPoint, isPathALoop, } from "../math";
|
|
31
|
-
import { calculateScrollCenter,
|
|
31
|
+
import { calculateScrollCenter, getElementsWithinSelection, getNormalizedZoom, getSelectedElements, hasBackground, isSomeElementSelected, } from "../scene";
|
|
32
32
|
import Scene from "../scene/Scene";
|
|
33
33
|
import { getStateForZoom } from "../scene/zoom";
|
|
34
34
|
import { findShapeByKey } from "../shapes";
|
|
35
|
-
import {
|
|
35
|
+
import { getClosedCurveShape, getCurveShape, getEllipseShape, getFreedrawShape, getPolygonShape, } from "../../utils/geometry/shape";
|
|
36
|
+
import { isPointInShape } from "../../utils/collision";
|
|
37
|
+
import { debounce, distance, getFontString, getNearestScrollableContainer, isInputLike, isToolIcon, isWritableElement, sceneCoordsToViewportCoords, tupleToCoors, viewportCoordsToSceneCoords, wrapEvent, updateObject, updateActiveTool, getShortcutKey, isTransparent, easeToValuesRAF, muteFSAbortError, isTestEnv, easeOut, arrayToMap, updateStable, addEventListener, normalizeEOL, getDateTime, } from "../utils";
|
|
36
38
|
import { createSrcDoc, embeddableURLValidator, maybeParseEmbedSrc, getEmbedLink, } from "../element/embeddable";
|
|
37
39
|
import { ContextMenu, CONTEXT_MENU_SEPARATOR, } from "./ContextMenu";
|
|
38
40
|
import LayerUI from "./LayerUI";
|
|
@@ -42,8 +44,7 @@ import { dataURLToFile, generateIdFromFile, getDataURL, getFileFromEvent, ImageU
|
|
|
42
44
|
import { getInitializedImageElements, loadHTMLImageElement, normalizeSVG, updateImageCache as _updateImageCache, } from "../element/image";
|
|
43
45
|
import throttle from "lodash.throttle";
|
|
44
46
|
import { fileOpen } from "../data/filesystem";
|
|
45
|
-
import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx,
|
|
46
|
-
import { isHittingElementNotConsideringBoundingBox } from "../element/collision";
|
|
47
|
+
import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, isMeasureTextSupported, isValidTextContainer, } from "../element/textElement";
|
|
47
48
|
import { showHyperlinkTooltip, hideHyperlinkToolip, Hyperlink, } from "../components/hyperlink/Hyperlink";
|
|
48
49
|
import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
|
|
49
50
|
import { shouldShowBoundingBox } from "../element/transformHandles";
|
|
@@ -82,8 +83,10 @@ import { AnimatedTrail } from "../animated-trail";
|
|
|
82
83
|
import { LaserTrails } from "../laser-trails";
|
|
83
84
|
import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
|
|
84
85
|
import { getRenderOpacity } from "../renderer/renderElement";
|
|
86
|
+
import { hitElementBoundText, hitElementBoundingBox, hitElementBoundingBoxOnly, hitElementItself, shouldTestInside, } from "../element/collision";
|
|
85
87
|
import { textWysiwyg } from "../element/textWysiwyg";
|
|
86
88
|
import { isOverScrollBars } from "../scene/scrollbars";
|
|
89
|
+
import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
|
|
87
90
|
import { isPointHittingLink, isPointHittingLinkIcon, } from "./hyperlink/helpers";
|
|
88
91
|
import { getShortcutFromShortcutName } from "../actions/shortcuts";
|
|
89
92
|
const AppContext = React.createContext(null);
|
|
@@ -495,7 +498,7 @@ class App extends React.Component {
|
|
|
495
498
|
html, body {
|
|
496
499
|
width: 100%;
|
|
497
500
|
height: 100%;
|
|
498
|
-
color: ${this.state.theme ===
|
|
501
|
+
color: ${this.state.theme === THEME.DARK ? "white" : "black"};
|
|
499
502
|
}
|
|
500
503
|
body {
|
|
501
504
|
display: flex;
|
|
@@ -650,7 +653,7 @@ class App extends React.Component {
|
|
|
650
653
|
? src.srcdoc(this.state.theme)
|
|
651
654
|
: undefined, src: src?.type !== "document" ? src?.link ?? "" : undefined,
|
|
652
655
|
// https://stackoverflow.com/q/18470015
|
|
653
|
-
scrolling: "no", referrerPolicy: "no-referrer-when-downgrade", title: "Excalidraw Embedded Content", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", allowFullScreen: true, sandbox: "allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-presentation allow-downloads
|
|
656
|
+
scrolling: "no", referrerPolicy: "no-referrer-when-downgrade", title: "Excalidraw Embedded Content", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", allowFullScreen: true, sandbox: `${src?.sandbox?.allowSameOrigin ? "allow-same-origin" : ""} allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-presentation allow-downloads` })) })] }) }, el.id));
|
|
654
657
|
}) }));
|
|
655
658
|
}
|
|
656
659
|
getFrameNameDOMId = (frameElement) => {
|
|
@@ -692,7 +695,7 @@ class App extends React.Component {
|
|
|
692
695
|
if (!this.state.frameRendering.enabled || !this.state.frameRendering.name) {
|
|
693
696
|
return null;
|
|
694
697
|
}
|
|
695
|
-
const isDarkTheme = this.state.theme ===
|
|
698
|
+
const isDarkTheme = this.state.theme === THEME.DARK;
|
|
696
699
|
let frameIndex = 0;
|
|
697
700
|
let magicFrameIndex = 0;
|
|
698
701
|
return this.scene.getNonDeletedFramesLikes().map((f) => {
|
|
@@ -1122,7 +1125,7 @@ class App extends React.Component {
|
|
|
1122
1125
|
opacity: 100,
|
|
1123
1126
|
locked: false,
|
|
1124
1127
|
});
|
|
1125
|
-
this.scene.
|
|
1128
|
+
this.scene.insertElement(frame);
|
|
1126
1129
|
for (const child of selectedElements) {
|
|
1127
1130
|
mutateElement(child, { frameId: frame.id });
|
|
1128
1131
|
}
|
|
@@ -1608,7 +1611,7 @@ class App extends React.Component {
|
|
|
1608
1611
|
gridSize: this.props.gridModeEnabled ? GRID_SIZE : null,
|
|
1609
1612
|
});
|
|
1610
1613
|
}
|
|
1611
|
-
this.excalidrawContainerRef.current?.classList.toggle("theme--dark", this.state.theme ===
|
|
1614
|
+
this.excalidrawContainerRef.current?.classList.toggle("theme--dark", this.state.theme === THEME.DARK);
|
|
1612
1615
|
if (this.state.editingLinearElement &&
|
|
1613
1616
|
!this.state.selectedElementIds[this.state.editingLinearElement.elementId]) {
|
|
1614
1617
|
// defer so that the commitToHistory flag isn't reset via current update
|
|
@@ -1636,7 +1639,7 @@ class App extends React.Component {
|
|
|
1636
1639
|
multiElement != null &&
|
|
1637
1640
|
isBindingEnabled(this.state) &&
|
|
1638
1641
|
isBindingElement(multiElement, false)) {
|
|
1639
|
-
maybeBindLinearElement(multiElement, this.state,
|
|
1642
|
+
maybeBindLinearElement(multiElement, this.state, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, elementsMap)), this);
|
|
1640
1643
|
}
|
|
1641
1644
|
this.history.record(this.state, elements);
|
|
1642
1645
|
// Do not notify consumers if we're still loading the scene. Among other
|
|
@@ -1894,16 +1897,15 @@ class App extends React.Component {
|
|
|
1894
1897
|
}), {
|
|
1895
1898
|
randomizeSeed: !opts.retainSeed,
|
|
1896
1899
|
});
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
];
|
|
1900
|
+
const prevElements = this.scene.getElementsIncludingDeleted();
|
|
1901
|
+
const nextElements = [...prevElements, ...newElements];
|
|
1902
|
+
syncMovedIndices(nextElements, arrayToMap(newElements));
|
|
1901
1903
|
const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
|
|
1902
1904
|
if (topLayerFrame) {
|
|
1903
1905
|
const eligibleElements = filterElementsEligibleAsFrameChildren(newElements, topLayerFrame);
|
|
1904
|
-
addElementsToFrame(
|
|
1906
|
+
addElementsToFrame(nextElements, eligibleElements, topLayerFrame);
|
|
1905
1907
|
}
|
|
1906
|
-
this.scene.replaceAllElements(
|
|
1908
|
+
this.scene.replaceAllElements(nextElements);
|
|
1907
1909
|
newElements.forEach((newElement) => {
|
|
1908
1910
|
if (isTextElement(newElement) && isBoundToContainer(newElement)) {
|
|
1909
1911
|
const container = getContainerElement(newElement, this.scene.getElementsMapIncludingDeleted());
|
|
@@ -2071,16 +2073,7 @@ class App extends React.Component {
|
|
|
2071
2073
|
if (textElements.length === 0) {
|
|
2072
2074
|
return;
|
|
2073
2075
|
}
|
|
2074
|
-
|
|
2075
|
-
if (frameId) {
|
|
2076
|
-
this.scene.insertElementsAtIndex(textElements, this.scene.getElementIndex(frameId));
|
|
2077
|
-
}
|
|
2078
|
-
else {
|
|
2079
|
-
this.scene.replaceAllElements([
|
|
2080
|
-
...this.scene.getElementsIncludingDeleted(),
|
|
2081
|
-
...textElements,
|
|
2082
|
-
]);
|
|
2083
|
-
}
|
|
2076
|
+
this.scene.insertElements(textElements);
|
|
2084
2077
|
this.setState({
|
|
2085
2078
|
selectedElementIds: makeNextSelectedElementIds(Object.fromEntries(textElements.map((el) => [el.id, true])), this.state),
|
|
2086
2079
|
});
|
|
@@ -2604,7 +2597,7 @@ class App extends React.Component {
|
|
|
2604
2597
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
2605
2598
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
2606
2599
|
isBindingEnabled(this.state)
|
|
2607
|
-
? bindOrUnbindSelectedElements(selectedElements, this
|
|
2600
|
+
? bindOrUnbindSelectedElements(selectedElements, this)
|
|
2608
2601
|
: unbindLinearElements(selectedElements, elementsMap);
|
|
2609
2602
|
this.setState({ suggestedBindings: [] });
|
|
2610
2603
|
}
|
|
@@ -2834,6 +2827,57 @@ class App extends React.Component {
|
|
|
2834
2827
|
}
|
|
2835
2828
|
return null;
|
|
2836
2829
|
}
|
|
2830
|
+
/**
|
|
2831
|
+
* get the pure geometric shape of an excalidraw element
|
|
2832
|
+
* which is then used for hit detection
|
|
2833
|
+
*/
|
|
2834
|
+
getElementShape(element) {
|
|
2835
|
+
switch (element.type) {
|
|
2836
|
+
case "rectangle":
|
|
2837
|
+
case "diamond":
|
|
2838
|
+
case "frame":
|
|
2839
|
+
case "magicframe":
|
|
2840
|
+
case "embeddable":
|
|
2841
|
+
case "image":
|
|
2842
|
+
case "iframe":
|
|
2843
|
+
case "text":
|
|
2844
|
+
case "selection":
|
|
2845
|
+
return getPolygonShape(element);
|
|
2846
|
+
case "arrow":
|
|
2847
|
+
case "line": {
|
|
2848
|
+
const roughShape = ShapeCache.get(element)?.[0] ??
|
|
2849
|
+
ShapeCache.generateElementShape(element, null)[0];
|
|
2850
|
+
const [, , , , cx, cy] = getElementAbsoluteCoords(element, this.scene.getNonDeletedElementsMap());
|
|
2851
|
+
return shouldTestInside(element)
|
|
2852
|
+
? getClosedCurveShape(element, roughShape, [element.x, element.y], element.angle, [cx, cy])
|
|
2853
|
+
: getCurveShape(roughShape, [element.x, element.y], element.angle, [
|
|
2854
|
+
cx,
|
|
2855
|
+
cy,
|
|
2856
|
+
]);
|
|
2857
|
+
}
|
|
2858
|
+
case "ellipse":
|
|
2859
|
+
return getEllipseShape(element);
|
|
2860
|
+
case "freedraw": {
|
|
2861
|
+
const [, , , , cx, cy] = getElementAbsoluteCoords(element, this.scene.getNonDeletedElementsMap());
|
|
2862
|
+
return getFreedrawShape(element, [cx, cy], shouldTestInside(element));
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
getBoundTextShape(element) {
|
|
2867
|
+
const boundTextElement = getBoundTextElement(element, this.scene.getNonDeletedElementsMap());
|
|
2868
|
+
if (boundTextElement) {
|
|
2869
|
+
if (element.type === "arrow") {
|
|
2870
|
+
return this.getElementShape({
|
|
2871
|
+
...boundTextElement,
|
|
2872
|
+
// arrow's bound text accurate position is not stored in the element's property
|
|
2873
|
+
// but rather calculated and returned from the following static method
|
|
2874
|
+
...LinearElementEditor.getBoundTextElementPosition(element, boundTextElement, this.scene.getNonDeletedElementsMap()),
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
return this.getElementShape(boundTextElement);
|
|
2878
|
+
}
|
|
2879
|
+
return null;
|
|
2880
|
+
}
|
|
2837
2881
|
getElementAtPosition(x, y, opts) {
|
|
2838
2882
|
const allHitElements = this.getElementsAtPosition(x, y, opts?.includeBoundTextElement, opts?.includeLockedElements);
|
|
2839
2883
|
if (allHitElements.length > 1) {
|
|
@@ -2847,9 +2891,9 @@ class App extends React.Component {
|
|
|
2847
2891
|
const elementWithHighestZIndex = allHitElements[allHitElements.length - 1];
|
|
2848
2892
|
// If we're hitting element with highest z-index only on its bounding box
|
|
2849
2893
|
// while also hitting other element figure, the latter should be considered.
|
|
2850
|
-
return
|
|
2851
|
-
?
|
|
2852
|
-
:
|
|
2894
|
+
return isPointInShape([x, y], this.getElementShape(elementWithHighestZIndex))
|
|
2895
|
+
? elementWithHighestZIndex
|
|
2896
|
+
: allHitElements[allHitElements.length - 2];
|
|
2853
2897
|
}
|
|
2854
2898
|
if (allHitElements.length === 1) {
|
|
2855
2899
|
return allHitElements[0];
|
|
@@ -2857,15 +2901,17 @@ class App extends React.Component {
|
|
|
2857
2901
|
return null;
|
|
2858
2902
|
}
|
|
2859
2903
|
getElementsAtPosition(x, y, includeBoundTextElement = false, includeLockedElements = false) {
|
|
2860
|
-
const
|
|
2904
|
+
const iframeLikes = [];
|
|
2905
|
+
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
2906
|
+
const elements = (includeBoundTextElement && includeLockedElements
|
|
2861
2907
|
? this.scene.getNonDeletedElements()
|
|
2862
2908
|
: this.scene
|
|
2863
2909
|
.getNonDeletedElements()
|
|
2864
2910
|
.filter((element) => (includeLockedElements || !element.locked) &&
|
|
2865
2911
|
(includeBoundTextElement ||
|
|
2866
|
-
!(isTextElement(element) && element.containerId)))
|
|
2867
|
-
|
|
2868
|
-
|
|
2912
|
+
!(isTextElement(element) && element.containerId))))
|
|
2913
|
+
.filter((el) => this.hitElement(x, y, el))
|
|
2914
|
+
.filter((element) => {
|
|
2869
2915
|
// hitting a frame's element from outside the frame is not considered a hit
|
|
2870
2916
|
const containingFrame = getContainingFrame(element, elementsMap);
|
|
2871
2917
|
return containingFrame &&
|
|
@@ -2873,8 +2919,80 @@ class App extends React.Component {
|
|
|
2873
2919
|
this.state.frameRendering.clip
|
|
2874
2920
|
? isCursorInFrame({ x, y }, containingFrame, elementsMap)
|
|
2875
2921
|
: true;
|
|
2922
|
+
})
|
|
2923
|
+
.filter((el) => {
|
|
2924
|
+
// The parameter elements comes ordered from lower z-index to higher.
|
|
2925
|
+
// We want to preserve that order on the returned array.
|
|
2926
|
+
// Exception being embeddables which should be on top of everything else in
|
|
2927
|
+
// terms of hit testing.
|
|
2928
|
+
if (isIframeElement(el)) {
|
|
2929
|
+
iframeLikes.push(el);
|
|
2930
|
+
return false;
|
|
2931
|
+
}
|
|
2932
|
+
return true;
|
|
2933
|
+
})
|
|
2934
|
+
.concat(iframeLikes);
|
|
2935
|
+
return elements;
|
|
2936
|
+
}
|
|
2937
|
+
getHitThreshold() {
|
|
2938
|
+
return 10 / this.state.zoom.value;
|
|
2939
|
+
}
|
|
2940
|
+
hitElement(x, y, element, considerBoundingBox = true) {
|
|
2941
|
+
// if the element is selected, then hit test is done against its bounding box
|
|
2942
|
+
if (considerBoundingBox &&
|
|
2943
|
+
this.state.selectedElementIds[element.id] &&
|
|
2944
|
+
shouldShowBoundingBox([element], this.state)) {
|
|
2945
|
+
return hitElementBoundingBox(x, y, element, this.scene.getNonDeletedElementsMap(), this.getHitThreshold());
|
|
2946
|
+
}
|
|
2947
|
+
// take bound text element into consideration for hit collision as well
|
|
2948
|
+
const hitBoundTextOfElement = hitElementBoundText(x, y, this.getBoundTextShape(element));
|
|
2949
|
+
if (hitBoundTextOfElement) {
|
|
2950
|
+
return true;
|
|
2951
|
+
}
|
|
2952
|
+
return hitElementItself({
|
|
2953
|
+
x,
|
|
2954
|
+
y,
|
|
2955
|
+
element,
|
|
2956
|
+
shape: this.getElementShape(element),
|
|
2957
|
+
threshold: this.getHitThreshold(),
|
|
2958
|
+
frameNameBound: isFrameLikeElement(element)
|
|
2959
|
+
? this.frameNameBoundsCache.get(element)
|
|
2960
|
+
: null,
|
|
2876
2961
|
});
|
|
2877
2962
|
}
|
|
2963
|
+
getTextBindableContainerAtPosition(x, y) {
|
|
2964
|
+
const elements = this.scene.getNonDeletedElements();
|
|
2965
|
+
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
2966
|
+
if (selectedElements.length === 1) {
|
|
2967
|
+
return isTextBindableContainer(selectedElements[0], false)
|
|
2968
|
+
? selectedElements[0]
|
|
2969
|
+
: null;
|
|
2970
|
+
}
|
|
2971
|
+
let hitElement = null;
|
|
2972
|
+
// We need to do hit testing from front (end of the array) to back (beginning of the array)
|
|
2973
|
+
for (let index = elements.length - 1; index >= 0; --index) {
|
|
2974
|
+
if (elements[index].isDeleted) {
|
|
2975
|
+
continue;
|
|
2976
|
+
}
|
|
2977
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[index], this.scene.getNonDeletedElementsMap());
|
|
2978
|
+
if (isArrowElement(elements[index]) &&
|
|
2979
|
+
hitElementItself({
|
|
2980
|
+
x,
|
|
2981
|
+
y,
|
|
2982
|
+
element: elements[index],
|
|
2983
|
+
shape: this.getElementShape(elements[index]),
|
|
2984
|
+
threshold: this.getHitThreshold(),
|
|
2985
|
+
})) {
|
|
2986
|
+
hitElement = elements[index];
|
|
2987
|
+
break;
|
|
2988
|
+
}
|
|
2989
|
+
else if (x1 < x && x < x2 && y1 < y && y < y2) {
|
|
2990
|
+
hitElement = elements[index];
|
|
2991
|
+
break;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
return isTextBindableContainer(hitElement, false) ? hitElement : null;
|
|
2995
|
+
}
|
|
2878
2996
|
startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, }) => {
|
|
2879
2997
|
let shouldBindToContainer = false;
|
|
2880
2998
|
let parentCenterPosition = insertAtParentCenter &&
|
|
@@ -2973,7 +3091,7 @@ class App extends React.Component {
|
|
|
2973
3091
|
this.scene.insertElementAtIndex(element, containerIndex + 1);
|
|
2974
3092
|
}
|
|
2975
3093
|
else {
|
|
2976
|
-
this.scene.
|
|
3094
|
+
this.scene.insertElement(element);
|
|
2977
3095
|
}
|
|
2978
3096
|
}
|
|
2979
3097
|
this.setState({
|
|
@@ -3032,11 +3150,17 @@ class App extends React.Component {
|
|
|
3032
3150
|
});
|
|
3033
3151
|
return;
|
|
3034
3152
|
}
|
|
3035
|
-
const container =
|
|
3153
|
+
const container = this.getTextBindableContainerAtPosition(sceneX, sceneY);
|
|
3036
3154
|
if (container) {
|
|
3037
3155
|
if (hasBoundTextElement(container) ||
|
|
3038
3156
|
!isTransparent(container.backgroundColor) ||
|
|
3039
|
-
|
|
3157
|
+
hitElementItself({
|
|
3158
|
+
x: sceneX,
|
|
3159
|
+
y: sceneY,
|
|
3160
|
+
element: container,
|
|
3161
|
+
shape: this.getElementShape(container),
|
|
3162
|
+
threshold: this.getHitThreshold(),
|
|
3163
|
+
})) {
|
|
3040
3164
|
const midPoint = getContainerCenter(container, this.state, this.scene.getNonDeletedElementsMap());
|
|
3041
3165
|
sceneX = midPoint.x;
|
|
3042
3166
|
sceneY = midPoint.y;
|
|
@@ -3091,7 +3215,7 @@ class App extends React.Component {
|
|
|
3091
3215
|
}
|
|
3092
3216
|
if (!customEvent?.defaultPrevented) {
|
|
3093
3217
|
const target = isLocalLink(url) ? "_self" : "_blank";
|
|
3094
|
-
const newWindow = window.open(undefined, target
|
|
3218
|
+
const newWindow = window.open(undefined, target);
|
|
3095
3219
|
// https://mathiasbynens.github.io/rel-noopener/
|
|
3096
3220
|
if (newWindow) {
|
|
3097
3221
|
newWindow.opener = null;
|
|
@@ -3419,7 +3543,7 @@ class App extends React.Component {
|
|
|
3419
3543
|
}
|
|
3420
3544
|
};
|
|
3421
3545
|
const distance = distance2d(pointerDownState.lastCoords.x, pointerDownState.lastCoords.y, scenePointer.x, scenePointer.y);
|
|
3422
|
-
const threshold =
|
|
3546
|
+
const threshold = this.getHitThreshold();
|
|
3423
3547
|
const point = { ...pointerDownState.lastCoords };
|
|
3424
3548
|
let samplingInterval = 0;
|
|
3425
3549
|
while (samplingInterval <= distance) {
|
|
@@ -3465,14 +3589,18 @@ class App extends React.Component {
|
|
|
3465
3589
|
handleHoverSelectedLinearElement(linearElementEditor, scenePointerX, scenePointerY) {
|
|
3466
3590
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
3467
3591
|
const element = LinearElementEditor.getElement(linearElementEditor.elementId, elementsMap);
|
|
3468
|
-
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
3469
3592
|
if (!element) {
|
|
3470
3593
|
return;
|
|
3471
3594
|
}
|
|
3472
3595
|
if (this.state.selectedLinearElement) {
|
|
3473
3596
|
let hoverPointIndex = -1;
|
|
3474
3597
|
let segmentMidPointHoveredCoords = null;
|
|
3475
|
-
if (
|
|
3598
|
+
if (hitElementItself({
|
|
3599
|
+
x: scenePointerX,
|
|
3600
|
+
y: scenePointerY,
|
|
3601
|
+
element,
|
|
3602
|
+
shape: this.getElementShape(element),
|
|
3603
|
+
})) {
|
|
3476
3604
|
hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(element, elementsMap, this.state.zoom, scenePointerX, scenePointerY);
|
|
3477
3605
|
segmentMidPointHoveredCoords =
|
|
3478
3606
|
LinearElementEditor.getSegmentMidpointHitCoords(linearElementEditor, { x: scenePointerX, y: scenePointerY }, this.state, this.scene.getNonDeletedElementsMap());
|
|
@@ -3483,12 +3611,7 @@ class App extends React.Component {
|
|
|
3483
3611
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
|
3484
3612
|
}
|
|
3485
3613
|
}
|
|
3486
|
-
else if (
|
|
3487
|
-
isHittingElementBoundingBoxWithoutHittingElement(element, this.state, this.frameNameBoundsCache, scenePointerX, scenePointerY, elementsMap)) {
|
|
3488
|
-
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
|
3489
|
-
}
|
|
3490
|
-
else if (boundTextElement &&
|
|
3491
|
-
hitTest(boundTextElement, this.state, this.frameNameBoundsCache, scenePointerX, scenePointerY, this.scene.getNonDeletedElementsMap())) {
|
|
3614
|
+
else if (this.hitElement(scenePointerX, scenePointerY, element)) {
|
|
3492
3615
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
|
3493
3616
|
}
|
|
3494
3617
|
if (this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex) {
|
|
@@ -4019,7 +4142,7 @@ class App extends React.Component {
|
|
|
4019
4142
|
else {
|
|
4020
4143
|
if (this.state.selectedLinearElement) {
|
|
4021
4144
|
const linearElementEditor = this.state.editingLinearElement || this.state.selectedLinearElement;
|
|
4022
|
-
const ret = LinearElementEditor.handlePointerDown(event, this.state, this.history, pointerDownState.origin, linearElementEditor, this
|
|
4145
|
+
const ret = LinearElementEditor.handlePointerDown(event, this.state, this.history, pointerDownState.origin, linearElementEditor, this);
|
|
4023
4146
|
if (ret.hitElement) {
|
|
4024
4147
|
pointerDownState.hit.element = ret.hitElement;
|
|
4025
4148
|
}
|
|
@@ -4180,7 +4303,7 @@ class App extends React.Component {
|
|
|
4180
4303
|
return false;
|
|
4181
4304
|
}
|
|
4182
4305
|
// How many pixels off the shape boundary we still consider a hit
|
|
4183
|
-
const threshold =
|
|
4306
|
+
const threshold = this.getHitThreshold();
|
|
4184
4307
|
const [x1, y1, x2, y2] = getCommonBounds(selectedElements);
|
|
4185
4308
|
return (point.x > x1 - threshold &&
|
|
4186
4309
|
point.x < x2 + threshold &&
|
|
@@ -4200,7 +4323,7 @@ class App extends React.Component {
|
|
|
4200
4323
|
includeBoundTextElement: true,
|
|
4201
4324
|
});
|
|
4202
4325
|
// FIXME
|
|
4203
|
-
let container =
|
|
4326
|
+
let container = this.getTextBindableContainerAtPosition(sceneX, sceneY);
|
|
4204
4327
|
if (hasBoundTextElement(element)) {
|
|
4205
4328
|
container = element;
|
|
4206
4329
|
sceneX = element.x + element.width / 2;
|
|
@@ -4258,8 +4381,8 @@ class App extends React.Component {
|
|
|
4258
4381
|
points: [[0, 0]],
|
|
4259
4382
|
pressures,
|
|
4260
4383
|
});
|
|
4261
|
-
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this
|
|
4262
|
-
this.scene.
|
|
4384
|
+
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
|
|
4385
|
+
this.scene.insertElement(element);
|
|
4263
4386
|
this.setState({
|
|
4264
4387
|
draggingElement: element,
|
|
4265
4388
|
editingElement: element,
|
|
@@ -4287,10 +4410,7 @@ class App extends React.Component {
|
|
|
4287
4410
|
width,
|
|
4288
4411
|
height,
|
|
4289
4412
|
});
|
|
4290
|
-
this.scene.
|
|
4291
|
-
...this.scene.getElementsIncludingDeleted(),
|
|
4292
|
-
element,
|
|
4293
|
-
]);
|
|
4413
|
+
this.scene.insertElement(element);
|
|
4294
4414
|
return element;
|
|
4295
4415
|
};
|
|
4296
4416
|
//create rectangle element with youtube top left on nearest grid point width / hight 640/360
|
|
@@ -4325,10 +4445,7 @@ class App extends React.Component {
|
|
|
4325
4445
|
height: embedLink.intrinsicSize.h,
|
|
4326
4446
|
link,
|
|
4327
4447
|
});
|
|
4328
|
-
this.scene.
|
|
4329
|
-
...this.scene.getElementsIncludingDeleted(),
|
|
4330
|
-
element,
|
|
4331
|
-
]);
|
|
4448
|
+
this.scene.insertElement(element);
|
|
4332
4449
|
return element;
|
|
4333
4450
|
};
|
|
4334
4451
|
createImageElement = ({ sceneX, sceneY, addToFrameUnderCursor = true, }) => {
|
|
@@ -4436,8 +4553,8 @@ class App extends React.Component {
|
|
|
4436
4553
|
mutateElement(element, {
|
|
4437
4554
|
points: [...element.points, [0, 0]],
|
|
4438
4555
|
});
|
|
4439
|
-
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this
|
|
4440
|
-
this.scene.
|
|
4556
|
+
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
|
|
4557
|
+
this.scene.insertElement(element);
|
|
4441
4558
|
this.setState({
|
|
4442
4559
|
draggingElement: element,
|
|
4443
4560
|
editingElement: element,
|
|
@@ -4497,7 +4614,7 @@ class App extends React.Component {
|
|
|
4497
4614
|
});
|
|
4498
4615
|
}
|
|
4499
4616
|
else {
|
|
4500
|
-
this.scene.
|
|
4617
|
+
this.scene.insertElement(element);
|
|
4501
4618
|
this.setState({
|
|
4502
4619
|
multiElement: null,
|
|
4503
4620
|
draggingElement: element,
|
|
@@ -4519,10 +4636,7 @@ class App extends React.Component {
|
|
|
4519
4636
|
const frame = type === TOOL_TYPE.magicframe
|
|
4520
4637
|
? newMagicFrameElement(constructorOpts)
|
|
4521
4638
|
: newFrameElement(constructorOpts);
|
|
4522
|
-
this.scene.
|
|
4523
|
-
...this.scene.getElementsIncludingDeleted(),
|
|
4524
|
-
frame,
|
|
4525
|
-
]);
|
|
4639
|
+
this.scene.insertElement(frame);
|
|
4526
4640
|
this.setState({
|
|
4527
4641
|
multiElement: null,
|
|
4528
4642
|
draggingElement: frame,
|
|
@@ -4780,6 +4894,7 @@ class App extends React.Component {
|
|
|
4780
4894
|
}
|
|
4781
4895
|
}
|
|
4782
4896
|
const nextSceneElements = [...nextElements, ...elementsToAppend];
|
|
4897
|
+
syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend));
|
|
4783
4898
|
bindTextToShapeAfterDuplication(nextElements, elementsToAppend, oldIdToDuplicatedId);
|
|
4784
4899
|
fixBindingsAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId, "duplicatesServeAsOld");
|
|
4785
4900
|
bindElementsToFramesAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId);
|
|
@@ -4971,7 +5086,7 @@ class App extends React.Component {
|
|
|
4971
5086
|
this.actionManager.executeAction(actionFinalize);
|
|
4972
5087
|
}
|
|
4973
5088
|
else {
|
|
4974
|
-
const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this
|
|
5089
|
+
const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this);
|
|
4975
5090
|
if (editingLinearElement !== this.state.editingLinearElement) {
|
|
4976
5091
|
this.setState({
|
|
4977
5092
|
editingLinearElement,
|
|
@@ -4990,7 +5105,7 @@ class App extends React.Component {
|
|
|
4990
5105
|
}
|
|
4991
5106
|
}
|
|
4992
5107
|
else {
|
|
4993
|
-
const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this
|
|
5108
|
+
const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this);
|
|
4994
5109
|
const { startBindingElement, endBindingElement } = linearElementEditor;
|
|
4995
5110
|
const element = this.scene.getElement(linearElementEditor.elementId);
|
|
4996
5111
|
if (isBindingElement(element)) {
|
|
@@ -5082,7 +5197,7 @@ class App extends React.Component {
|
|
|
5082
5197
|
else if (pointerDownState.drag.hasOccurred && !multiElement) {
|
|
5083
5198
|
if (isBindingEnabled(this.state) &&
|
|
5084
5199
|
isBindingElement(draggingElement, false)) {
|
|
5085
|
-
maybeBindLinearElement(draggingElement, this.state,
|
|
5200
|
+
maybeBindLinearElement(draggingElement, this.state, pointerCoords, this);
|
|
5086
5201
|
}
|
|
5087
5202
|
this.setState({ suggestedBindings: [], startBoundElement: null });
|
|
5088
5203
|
if (!activeTool.locked) {
|
|
@@ -5356,10 +5471,23 @@ class App extends React.Component {
|
|
|
5356
5471
|
}));
|
|
5357
5472
|
}
|
|
5358
5473
|
}
|
|
5359
|
-
if (
|
|
5474
|
+
if (
|
|
5475
|
+
// not dragged
|
|
5476
|
+
!pointerDownState.drag.hasOccurred &&
|
|
5477
|
+
// not resized
|
|
5360
5478
|
!this.state.isResizing &&
|
|
5479
|
+
// only hitting the bounding box of the previous hit element
|
|
5361
5480
|
((hitElement &&
|
|
5362
|
-
|
|
5481
|
+
hitElementBoundingBoxOnly({
|
|
5482
|
+
x: pointerDownState.origin.x,
|
|
5483
|
+
y: pointerDownState.origin.y,
|
|
5484
|
+
element: hitElement,
|
|
5485
|
+
shape: this.getElementShape(hitElement),
|
|
5486
|
+
threshold: this.getHitThreshold(),
|
|
5487
|
+
frameNameBound: isFrameLikeElement(hitElement)
|
|
5488
|
+
? this.frameNameBoundsCache.get(hitElement)
|
|
5489
|
+
: null,
|
|
5490
|
+
}, elementsMap)) ||
|
|
5363
5491
|
(!hitElement &&
|
|
5364
5492
|
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements))) {
|
|
5365
5493
|
if (this.state.editingLinearElement) {
|
|
@@ -5374,6 +5502,8 @@ class App extends React.Component {
|
|
|
5374
5502
|
activeEmbeddable: null,
|
|
5375
5503
|
});
|
|
5376
5504
|
}
|
|
5505
|
+
// reset cursor
|
|
5506
|
+
setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO);
|
|
5377
5507
|
return;
|
|
5378
5508
|
}
|
|
5379
5509
|
if (!activeTool.locked &&
|
|
@@ -5396,8 +5526,8 @@ class App extends React.Component {
|
|
|
5396
5526
|
}
|
|
5397
5527
|
if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
|
|
5398
5528
|
isBindingEnabled(this.state)
|
|
5399
|
-
? bindOrUnbindSelectedElements(this.scene.getSelectedElements(this.state), this
|
|
5400
|
-
: unbindLinearElements(this.scene.
|
|
5529
|
+
? bindOrUnbindSelectedElements(this.scene.getSelectedElements(this.state), this)
|
|
5530
|
+
: unbindLinearElements(this.scene.getNonDeletedElements(), elementsMap);
|
|
5401
5531
|
}
|
|
5402
5532
|
if (activeTool.type === "laser") {
|
|
5403
5533
|
this.laserTrails.endPath();
|
|
@@ -5551,7 +5681,7 @@ class App extends React.Component {
|
|
|
5551
5681
|
this.setState({ errorMessage: t("errors.imageToolNotSupported") });
|
|
5552
5682
|
return;
|
|
5553
5683
|
}
|
|
5554
|
-
this.scene.
|
|
5684
|
+
this.scene.insertElement(imageElement);
|
|
5555
5685
|
try {
|
|
5556
5686
|
return await this.initializeImage({
|
|
5557
5687
|
imageFile,
|
|
@@ -5740,7 +5870,7 @@ class App extends React.Component {
|
|
|
5740
5870
|
}
|
|
5741
5871
|
};
|
|
5742
5872
|
maybeSuggestBindingAtCursor = (pointerCoords) => {
|
|
5743
|
-
const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this
|
|
5873
|
+
const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this);
|
|
5744
5874
|
this.setState({
|
|
5745
5875
|
suggestedBindings: hoveredBindableElement != null ? [hoveredBindableElement] : [],
|
|
5746
5876
|
});
|
|
@@ -5755,7 +5885,7 @@ class App extends React.Component {
|
|
|
5755
5885
|
return;
|
|
5756
5886
|
}
|
|
5757
5887
|
const suggestedBindings = pointerCoords.reduce((acc, coords) => {
|
|
5758
|
-
const hoveredBindableElement = getHoveredElementForBinding(coords, this
|
|
5888
|
+
const hoveredBindableElement = getHoveredElementForBinding(coords, this);
|
|
5759
5889
|
if (hoveredBindableElement != null &&
|
|
5760
5890
|
!isLinearElementSimpleAndAlreadyBound(linearElement, oppositeBindingBoundElement?.id, hoveredBindableElement)) {
|
|
5761
5891
|
acc.push(hoveredBindableElement);
|
|
@@ -5768,7 +5898,7 @@ class App extends React.Component {
|
|
|
5768
5898
|
if (selectedElements.length > 50) {
|
|
5769
5899
|
return;
|
|
5770
5900
|
}
|
|
5771
|
-
const suggestedBindings = getEligibleElementsForBinding(selectedElements, this
|
|
5901
|
+
const suggestedBindings = getEligibleElementsForBinding(selectedElements, this);
|
|
5772
5902
|
this.setState({ suggestedBindings });
|
|
5773
5903
|
}
|
|
5774
5904
|
clearSelection(hitElement) {
|
|
@@ -6320,7 +6450,7 @@ export const createTestHook = () => {
|
|
|
6320
6450
|
return this.app?.scene.getElementsIncludingDeleted();
|
|
6321
6451
|
},
|
|
6322
6452
|
set(elements) {
|
|
6323
|
-
return this.app?.scene.replaceAllElements(elements);
|
|
6453
|
+
return this.app?.scene.replaceAllElements(syncInvalidIndices(elements));
|
|
6324
6454
|
},
|
|
6325
6455
|
},
|
|
6326
6456
|
});
|