@excalidraw/excalidraw 0.17.1-c0b80a0 → 0.17.1-c329470
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/dist/browser/dev/excalidraw-assets-dev/{chunk-JGDL4H2X.js → chunk-3DLVY5XU.js} +8272 -6864
- package/dist/browser/dev/excalidraw-assets-dev/chunk-3DLVY5XU.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-V7NFEZA6.js → chunk-NOAEU4NM.js} +9 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-NOAEU4NM.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-ZSVWGT55.js → en-7IBTMWBG.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-RJG3J34Y.js → image-N5AC7SEK.js} +2 -6
- package/dist/browser/dev/index.css +85 -50
- package/dist/browser/dev/index.css.map +3 -3
- package/dist/browser/dev/index.js +4375 -3766
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/{chunk-LDVEIXGO.js → chunk-7CSIPVOW.js} +2 -2
- package/dist/browser/prod/excalidraw-assets/chunk-TX3BU7T2.js +47 -0
- package/dist/browser/prod/excalidraw-assets/{en-UPNEHLDS.js → en-LOGQBETY.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/image-3V4U7GZE.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +40 -40
- package/dist/dev/index.css +85 -50
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +8688 -6706
- package/dist/dev/index.js.map +4 -4
- package/dist/{prod/locales/en-ZXYG7GCR.json → dev/locales/en-V6KXFSCK.json} +8 -1
- package/dist/excalidraw/actions/actionAlign.d.ts +7 -6
- package/dist/excalidraw/actions/actionAlign.js +14 -14
- package/dist/excalidraw/actions/actionClipboard.d.ts +7 -3
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +7 -3
- package/dist/excalidraw/actions/actionDeleteSelected.js +103 -34
- package/dist/excalidraw/actions/actionDuplicateSelection.js +105 -95
- package/dist/excalidraw/actions/actionFlip.js +16 -7
- package/dist/excalidraw/actions/actionFrame.d.ts +493 -0
- package/dist/excalidraw/actions/actionFrame.js +45 -2
- package/dist/excalidraw/actions/actionGroup.js +6 -4
- package/dist/excalidraw/actions/actionProperties.js +145 -116
- package/dist/excalidraw/actions/actionSelectAll.js +4 -3
- package/dist/excalidraw/actions/shortcuts.d.ts +1 -1
- package/dist/excalidraw/actions/shortcuts.js +1 -0
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/align.d.ts +2 -1
- package/dist/excalidraw/align.js +15 -6
- package/dist/excalidraw/clipboard.d.ts +27 -5
- package/dist/excalidraw/clipboard.js +55 -28
- package/dist/excalidraw/components/Actions.d.ts +2 -1
- package/dist/excalidraw/components/Actions.js +4 -2
- package/dist/excalidraw/components/ActiveConfirmDialog.d.ts +1 -1
- package/dist/excalidraw/components/ActiveConfirmDialog.js +2 -3
- package/dist/excalidraw/components/App.d.ts +1 -0
- package/dist/excalidraw/components/App.js +216 -111
- package/dist/excalidraw/components/ColorPicker/ColorInput.js +2 -3
- package/dist/excalidraw/components/ColorPicker/ColorPicker.js +2 -3
- package/dist/excalidraw/components/ColorPicker/CustomColorList.js +1 -1
- package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
- package/dist/excalidraw/components/ColorPicker/PickerColorList.js +1 -1
- package/dist/excalidraw/components/ColorPicker/ShadeList.js +1 -1
- package/dist/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +1 -1
- package/dist/excalidraw/components/ColorPicker/colorPickerUtils.js +1 -1
- package/dist/excalidraw/components/CommandPalette/CommandPalette.js +3 -3
- package/dist/excalidraw/components/ConfirmDialog.js +17 -5
- package/dist/excalidraw/components/Dialog.js +2 -3
- package/dist/excalidraw/components/EyeDropper.d.ts +1 -1
- package/dist/excalidraw/components/EyeDropper.js +1 -1
- package/dist/excalidraw/components/IconPicker.d.ts +2 -2
- package/dist/excalidraw/components/IconPicker.js +56 -53
- package/dist/excalidraw/components/LayerUI.js +6 -6
- package/dist/excalidraw/components/LibraryMenu.d.ts +2 -16
- package/dist/excalidraw/components/LibraryMenu.js +70 -28
- package/dist/excalidraw/components/LibraryMenuHeaderContent.js +4 -5
- package/dist/excalidraw/components/MobileMenu.js +1 -1
- package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirm.js +2 -3
- package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
- package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.js +2 -3
- package/dist/excalidraw/components/Range.d.ts +9 -0
- package/dist/excalidraw/components/Range.js +24 -0
- package/dist/excalidraw/components/SearchMenu.d.ts +1 -1
- package/dist/excalidraw/components/SearchMenu.js +3 -4
- package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
- package/dist/excalidraw/components/Sidebar/Sidebar.js +2 -3
- package/dist/excalidraw/components/Stats/Collapsible.d.ts +2 -1
- package/dist/excalidraw/components/Stats/Collapsible.js +2 -2
- package/dist/excalidraw/components/Stats/Dimension.js +94 -8
- package/dist/excalidraw/components/Stats/MultiDimension.js +8 -5
- package/dist/excalidraw/components/Stats/Position.js +63 -3
- package/dist/excalidraw/components/Stats/index.js +21 -4
- package/dist/excalidraw/components/Stats/utils.d.ts +1 -1
- package/dist/excalidraw/components/Stats/utils.js +2 -55
- package/dist/excalidraw/components/TTDDialog/TTDDialog.js +1 -1
- package/dist/excalidraw/components/ToolButton.js +4 -9
- package/dist/excalidraw/components/hoc/withInternalFallback.js +3 -3
- package/dist/excalidraw/components/hyperlink/Hyperlink.js +6 -12
- package/dist/excalidraw/components/icons.d.ts +9 -0
- package/dist/excalidraw/components/icons.js +4 -4
- package/dist/excalidraw/components/main-menu/DefaultItems.js +2 -3
- package/dist/excalidraw/constants.d.ts +5 -1
- package/dist/excalidraw/constants.js +9 -1
- package/dist/excalidraw/context/tunnels.d.ts +2 -1
- package/dist/excalidraw/context/tunnels.js +3 -1
- package/dist/excalidraw/data/blob.d.ts +1 -0
- package/dist/excalidraw/data/blob.js +7 -3
- package/dist/excalidraw/data/filesystem.d.ts +2 -1
- package/dist/excalidraw/data/filesystem.js +1 -0
- package/dist/excalidraw/data/image.d.ts +0 -6
- package/dist/excalidraw/data/image.js +1 -43
- package/dist/excalidraw/data/index.js +6 -6
- package/dist/excalidraw/data/library.d.ts +9 -3
- package/dist/excalidraw/data/library.js +43 -6
- package/dist/excalidraw/data/restore.js +26 -8
- package/dist/excalidraw/data/url.d.ts +0 -1
- package/dist/excalidraw/data/url.js +2 -4
- package/dist/excalidraw/editor-jotai.d.ts +56 -0
- package/dist/excalidraw/editor-jotai.js +8 -0
- package/dist/excalidraw/element/binding.d.ts +9 -6
- package/dist/excalidraw/element/binding.js +124 -44
- package/dist/excalidraw/element/bounds.js +10 -0
- package/dist/excalidraw/element/cropElement.d.ts +5 -0
- package/dist/excalidraw/element/cropElement.js +28 -1
- package/dist/excalidraw/element/dragElements.js +13 -7
- package/dist/excalidraw/element/elbowArrow.d.ts +16 -0
- package/dist/excalidraw/element/elbowArrow.js +1268 -0
- package/dist/excalidraw/element/embeddable.js +4 -5
- package/dist/excalidraw/element/flowchart.d.ts +1 -1
- package/dist/excalidraw/element/flowchart.js +25 -9
- package/dist/excalidraw/element/heading.d.ts +5 -1
- package/dist/excalidraw/element/heading.js +5 -1
- package/dist/excalidraw/element/image.js +19 -5
- package/dist/excalidraw/element/linearElementEditor.d.ts +9 -10
- package/dist/excalidraw/element/linearElementEditor.js +97 -38
- package/dist/excalidraw/element/mutateElement.d.ts +3 -1
- package/dist/excalidraw/element/mutateElement.js +31 -4
- package/dist/excalidraw/element/newElement.d.ts +8 -12
- package/dist/excalidraw/element/newElement.js +36 -21
- package/dist/excalidraw/element/resizeElements.d.ts +20 -5
- package/dist/excalidraw/element/resizeElements.js +593 -361
- package/dist/excalidraw/element/sortElements.js +1 -4
- package/dist/excalidraw/element/types.d.ts +23 -1
- package/dist/excalidraw/fonts/Fonts.d.ts +0 -16
- package/dist/excalidraw/fonts/Fonts.js +6 -31
- package/dist/excalidraw/frame.d.ts +11 -5
- package/dist/excalidraw/frame.js +146 -35
- package/dist/excalidraw/groups.js +3 -0
- package/dist/excalidraw/hooks/useLibraryItemSvg.d.ts +1 -1
- package/dist/excalidraw/hooks/useLibraryItemSvg.js +2 -3
- package/dist/excalidraw/hooks/useScrollPosition.js +1 -1
- package/dist/excalidraw/i18n.js +3 -4
- package/dist/excalidraw/index.js +3 -4
- package/dist/excalidraw/locales/en.json +8 -1
- package/dist/excalidraw/renderer/interactiveScene.js +43 -32
- package/dist/excalidraw/renderer/staticScene.js +6 -4
- package/dist/excalidraw/renderer/staticSvgScene.js +1 -1
- package/dist/excalidraw/scene/Shape.js +40 -17
- package/dist/excalidraw/scene/comparisons.d.ts +0 -477
- package/dist/excalidraw/scene/comparisons.js +0 -37
- package/dist/excalidraw/scene/export.d.ts +7 -0
- package/dist/excalidraw/scene/export.js +107 -43
- package/dist/excalidraw/scene/index.d.ts +1 -1
- package/dist/excalidraw/scene/index.js +1 -1
- package/dist/excalidraw/scene/selection.js +4 -1
- package/dist/excalidraw/types.d.ts +15 -0
- package/dist/excalidraw/utility-types.d.ts +1 -0
- package/dist/excalidraw/utils.d.ts +8 -1
- package/dist/excalidraw/utils.js +9 -0
- package/dist/excalidraw/visualdebug.d.ts +8 -1
- package/dist/excalidraw/visualdebug.js +3 -0
- package/dist/math/line.d.ts +19 -0
- package/dist/math/line.js +32 -3
- package/dist/math/point.d.ts +10 -0
- package/dist/math/point.js +12 -1
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +29 -44
- package/dist/{dev/locales/en-ZXYG7GCR.json → prod/locales/en-V6KXFSCK.json} +8 -1
- package/package.json +5 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-JGDL4H2X.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-V7NFEZA6.js.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-S2XKB3DE.js +0 -62
- package/dist/browser/prod/excalidraw-assets/image-OFI2YYMP.js +0 -1
- package/dist/excalidraw/element/routing.d.ts +0 -12
- package/dist/excalidraw/element/routing.js +0 -642
- package/dist/excalidraw/jotai.d.ts +0 -34
- package/dist/excalidraw/jotai.js +0 -18
- /package/dist/browser/dev/excalidraw-assets-dev/{en-ZSVWGT55.js.map → en-7IBTMWBG.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-RJG3J34Y.js.map → image-N5AC7SEK.js.map} +0 -0
|
@@ -20,7 +20,7 @@ import { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfter
|
|
|
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, newArrowElement, } from "../element/newElement";
|
|
23
|
-
import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isIframeElement, isIframeLikeElement, isMagicFrameElement, isTextBindableContainer, isElbowArrow, isFlowchartNodeElement, } from "../element/typeChecks";
|
|
23
|
+
import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isIframeElement, isIframeLikeElement, isMagicFrameElement, isTextBindableContainer, isElbowArrow, isFlowchartNodeElement, isBindableElement, } 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";
|
|
@@ -33,7 +33,7 @@ import { getStateForZoom } from "../scene/zoom";
|
|
|
33
33
|
import { findShapeByKey, getBoundTextShape, getCornerRadius, getElementShape, isPathALoop, } from "../shapes";
|
|
34
34
|
import { getSelectionBoxShape } from "../../utils/geometry/shape";
|
|
35
35
|
import { isPointInShape } from "../../utils/collision";
|
|
36
|
-
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,
|
|
36
|
+
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, } from "../utils";
|
|
37
37
|
import { createSrcDoc, embeddableURLValidator, maybeParseEmbedSrc, getEmbedLink, } from "../element/embeddable";
|
|
38
38
|
import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu";
|
|
39
39
|
import LayerUI from "./LayerUI";
|
|
@@ -52,9 +52,9 @@ import { Fonts, getLineHeight } from "../fonts";
|
|
|
52
52
|
import { getFrameChildren, isCursorInFrame, bindElementsToFramesAfterDuplication, addElementsToFrame, replaceAllElementsInFrame, removeElementsFromFrame, getElementsInResizingFrame, getElementsInNewFrame, getContainingFrame, elementOverlapsWithFrame, updateFrameMembershipOfSelectedElements, isElementInFrame, getFrameLikeTitle, getElementsOverlappingFrame, filterElementsEligibleAsFrameChildren, } from "../frame";
|
|
53
53
|
import { excludeElementsInFramesFromSelection, makeNextSelectedElementIds, } from "../scene/selection";
|
|
54
54
|
import { actionPaste } from "../actions/actionClipboard";
|
|
55
|
-
import { actionRemoveAllElementsFromFrame, actionSelectAllElementsInFrame, } from "../actions/actionFrame";
|
|
55
|
+
import { actionRemoveAllElementsFromFrame, actionSelectAllElementsInFrame, actionWrapSelectionInFrame, } from "../actions/actionFrame";
|
|
56
56
|
import { actionToggleHandTool, zoomToFit } from "../actions/actionCanvas";
|
|
57
|
-
import {
|
|
57
|
+
import { editorJotaiStore } from "../editor-jotai";
|
|
58
58
|
import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
|
|
59
59
|
import { ImageSceneDataError } from "../errors";
|
|
60
60
|
import { getSnapLinesAtPointer, snapDraggedElements, isActiveToolNonLinearSnappable, snapNewElement, snapResizingElements, isSnappingEnabled, getVisibleGaps, getReferenceSnapPoints, SnapCache, isGridModeEnabled, getGridPoint, } from "../snapping";
|
|
@@ -90,7 +90,6 @@ import { actionTextAutoResize } from "../actions/actionTextAutoResize";
|
|
|
90
90
|
import { getVisibleSceneBounds } from "../element/bounds";
|
|
91
91
|
import { isMaybeMermaidDefinition } from "../mermaid";
|
|
92
92
|
import NewElementCanvas from "./canvases/NewElementCanvas";
|
|
93
|
-
import { mutateElbowArrow, updateElbowArrow } from "../element/routing";
|
|
94
93
|
import { FlowChartCreator, FlowChartNavigator, getLinkDirectionFromKey, } from "../element/flowchart";
|
|
95
94
|
import { searchItemInFocusAtom } from "./SearchMenu";
|
|
96
95
|
import { clamp, pointFrom, pointDistance, vector, pointRotateRads, vectorScale, vectorFromPoint, vectorSubtract, vectorDot, vectorNormalize, } from "../../math";
|
|
@@ -714,8 +713,17 @@ class App extends React.Component {
|
|
|
714
713
|
*/
|
|
715
714
|
_cache: new Map(),
|
|
716
715
|
};
|
|
716
|
+
resetEditingFrame = (frame) => {
|
|
717
|
+
if (frame) {
|
|
718
|
+
mutateElement(frame, { name: frame.name?.trim() || null });
|
|
719
|
+
}
|
|
720
|
+
this.setState({ editingFrame: null });
|
|
721
|
+
};
|
|
717
722
|
renderFrameNames = () => {
|
|
718
723
|
if (!this.state.frameRendering.enabled || !this.state.frameRendering.name) {
|
|
724
|
+
if (this.state.editingFrame) {
|
|
725
|
+
this.resetEditingFrame(null);
|
|
726
|
+
}
|
|
719
727
|
return null;
|
|
720
728
|
}
|
|
721
729
|
const isDarkTheme = this.state.theme === THEME.DARK;
|
|
@@ -727,15 +735,14 @@ class App extends React.Component {
|
|
|
727
735
|
scrollY: this.state.scrollY,
|
|
728
736
|
zoom: this.state.zoom,
|
|
729
737
|
}, this.scene.getNonDeletedElementsMap())) {
|
|
738
|
+
if (this.state.editingFrame === f.id) {
|
|
739
|
+
this.resetEditingFrame(f);
|
|
740
|
+
}
|
|
730
741
|
// if frame not visible, don't render its name
|
|
731
742
|
return null;
|
|
732
743
|
}
|
|
733
744
|
const { x: x1, y: y1 } = sceneCoordsToViewportCoords({ sceneX: f.x, sceneY: f.y }, this.state);
|
|
734
745
|
const FRAME_NAME_EDIT_PADDING = 6;
|
|
735
|
-
const reset = () => {
|
|
736
|
-
mutateElement(f, { name: f.name?.trim() || null });
|
|
737
|
-
this.setState({ editingFrame: null });
|
|
738
|
-
};
|
|
739
746
|
let frameNameJSX;
|
|
740
747
|
const frameName = getFrameLikeTitle(f);
|
|
741
748
|
if (f.id === this.state.editingFrame) {
|
|
@@ -744,12 +751,12 @@ class App extends React.Component {
|
|
|
744
751
|
mutateElement(f, {
|
|
745
752
|
name: e.target.value,
|
|
746
753
|
});
|
|
747
|
-
}, onFocus: (e) => e.target.select(), onBlur: () =>
|
|
754
|
+
}, onFocus: (e) => e.target.select(), onBlur: () => this.resetEditingFrame(f), onKeyDown: (event) => {
|
|
748
755
|
// for some inexplicable reason, `onBlur` triggered on ESC
|
|
749
756
|
// does not reset `state.editingFrame` despite being called,
|
|
750
757
|
// and we need to reset it here as well
|
|
751
758
|
if (event.key === KEYS.ESCAPE || event.key === KEYS.ENTER) {
|
|
752
|
-
|
|
759
|
+
this.resetEditingFrame(f);
|
|
753
760
|
}
|
|
754
761
|
}, style: {
|
|
755
762
|
background: this.state.viewBackgroundColor,
|
|
@@ -830,13 +837,18 @@ class App extends React.Component {
|
|
|
830
837
|
});
|
|
831
838
|
this.visibleElements = visibleElements;
|
|
832
839
|
const allElementsMap = this.scene.getNonDeletedElementsMap();
|
|
833
|
-
const shouldBlockPointerEvents =
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
this.state.
|
|
840
|
+
const shouldBlockPointerEvents =
|
|
841
|
+
// default back to `--ui-pointerEvents` flow if setPointerCapture
|
|
842
|
+
// not supported
|
|
843
|
+
"setPointerCapture" in HTMLElement.prototype
|
|
844
|
+
? false
|
|
845
|
+
: this.state.selectionElement ||
|
|
846
|
+
this.state.newElement ||
|
|
847
|
+
this.state.selectedElementsAreBeingDragged ||
|
|
848
|
+
this.state.resizingElement ||
|
|
849
|
+
(this.state.activeTool.type === "laser" &&
|
|
850
|
+
// technically we can just test on this once we make it more safe
|
|
851
|
+
this.state.cursorButton === "down");
|
|
840
852
|
const firstSelectedElement = selectedElements[0];
|
|
841
853
|
return (_jsx("div", { className: clsx("excalidraw excalidraw-container", {
|
|
842
854
|
"excalidraw--view-mode": this.state.viewModeEnabled ||
|
|
@@ -1094,7 +1106,7 @@ class App extends React.Component {
|
|
|
1094
1106
|
}
|
|
1095
1107
|
};
|
|
1096
1108
|
openEyeDropper = ({ type }) => {
|
|
1097
|
-
|
|
1109
|
+
editorJotaiStore.set(activeEyeDropperAtom, {
|
|
1098
1110
|
swapPreviewOnAlt: true,
|
|
1099
1111
|
colorPickerType: type === "stroke" ? "elementStroke" : "elementBackground",
|
|
1100
1112
|
onSelect: (color, event) => {
|
|
@@ -1902,30 +1914,7 @@ class App extends React.Component {
|
|
|
1902
1914
|
event?.preventDefault();
|
|
1903
1915
|
});
|
|
1904
1916
|
addElementsFromPasteOrLibrary = (opts) => {
|
|
1905
|
-
|
|
1906
|
-
if (isElbowArrow(el)) {
|
|
1907
|
-
const startEndElements = [
|
|
1908
|
-
el.startBinding &&
|
|
1909
|
-
elements.find((l) => l.id === el.startBinding?.elementId),
|
|
1910
|
-
el.endBinding &&
|
|
1911
|
-
elements.find((l) => l.id === el.endBinding?.elementId),
|
|
1912
|
-
];
|
|
1913
|
-
const startBinding = startEndElements[0] ? el.startBinding : null;
|
|
1914
|
-
const endBinding = startEndElements[1] ? el.endBinding : null;
|
|
1915
|
-
return {
|
|
1916
|
-
...el,
|
|
1917
|
-
...updateElbowArrow({
|
|
1918
|
-
...el,
|
|
1919
|
-
startBinding,
|
|
1920
|
-
endBinding,
|
|
1921
|
-
}, toBrandedType(new Map(startEndElements
|
|
1922
|
-
.filter((x) => x != null)
|
|
1923
|
-
.map((el) => [el.id, el]))), [el.points[0], el.points[el.points.length - 1]]),
|
|
1924
|
-
};
|
|
1925
|
-
}
|
|
1926
|
-
return el;
|
|
1927
|
-
});
|
|
1928
|
-
elements = restoreElements(elements, null, undefined);
|
|
1917
|
+
const elements = restoreElements(opts.elements, null, undefined);
|
|
1929
1918
|
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
|
|
1930
1919
|
const elementsCenterX = distance(minX, maxX) / 2;
|
|
1931
1920
|
const elementsCenterY = distance(minY, maxY) / 2;
|
|
@@ -1952,12 +1941,14 @@ class App extends React.Component {
|
|
|
1952
1941
|
randomizeSeed: !opts.retainSeed,
|
|
1953
1942
|
});
|
|
1954
1943
|
const prevElements = this.scene.getElementsIncludingDeleted();
|
|
1955
|
-
|
|
1944
|
+
let nextElements = [...prevElements, ...newElements];
|
|
1945
|
+
const mappedNewSceneElements = this.props.onDuplicate?.(nextElements, prevElements);
|
|
1946
|
+
nextElements = mappedNewSceneElements || nextElements;
|
|
1956
1947
|
syncMovedIndices(nextElements, arrayToMap(newElements));
|
|
1957
1948
|
const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
|
|
1958
1949
|
if (topLayerFrame) {
|
|
1959
1950
|
const eligibleElements = filterElementsEligibleAsFrameChildren(newElements, topLayerFrame);
|
|
1960
|
-
addElementsToFrame(nextElements, eligibleElements, topLayerFrame);
|
|
1951
|
+
addElementsToFrame(nextElements, eligibleElements, topLayerFrame, this.state);
|
|
1961
1952
|
}
|
|
1962
1953
|
this.scene.replaceAllElements(nextElements);
|
|
1963
1954
|
newElements.forEach((newElement) => {
|
|
@@ -1986,7 +1977,7 @@ class App extends React.Component {
|
|
|
1986
1977
|
// from library, not when pasting from clipboard. Alas.
|
|
1987
1978
|
openSidebar: this.state.openSidebar &&
|
|
1988
1979
|
this.device.editor.canFitSidebar &&
|
|
1989
|
-
|
|
1980
|
+
editorJotaiStore.get(isSidebarDockedAtom)
|
|
1990
1981
|
? this.state.openSidebar
|
|
1991
1982
|
: null,
|
|
1992
1983
|
...selectGroupsForSelectedElements({
|
|
@@ -2399,11 +2390,16 @@ class App extends React.Component {
|
|
|
2399
2390
|
addedFiles[fileData.id] = fileData;
|
|
2400
2391
|
nextFiles[fileData.id] = fileData;
|
|
2401
2392
|
if (fileData.mimeType === MIME_TYPES.svg) {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2393
|
+
try {
|
|
2394
|
+
const restoredDataURL = getDataURL_sync(normalizeSVG(dataURLToString(fileData.dataURL)), MIME_TYPES.svg);
|
|
2395
|
+
if (fileData.dataURL !== restoredDataURL) {
|
|
2396
|
+
// bump version so persistence layer can update the store
|
|
2397
|
+
fileData.version = (fileData.version ?? 1) + 1;
|
|
2398
|
+
fileData.dataURL = restoredDataURL;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
catch (error) {
|
|
2402
|
+
console.error(error);
|
|
2407
2403
|
}
|
|
2408
2404
|
}
|
|
2409
2405
|
}
|
|
@@ -2732,14 +2728,15 @@ class App extends React.Component {
|
|
|
2732
2728
|
mutateElement(element, {
|
|
2733
2729
|
x: element.x + offsetX,
|
|
2734
2730
|
y: element.y + offsetY,
|
|
2735
|
-
});
|
|
2731
|
+
}, false);
|
|
2736
2732
|
updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
|
|
2737
2733
|
simultaneouslyUpdated: selectedElements,
|
|
2738
2734
|
});
|
|
2739
2735
|
});
|
|
2740
2736
|
this.setState({
|
|
2741
|
-
suggestedBindings: getSuggestedBindingsForArrows(selectedElements.filter((element) => element.id !== elbowArrow?.id || step !== 0), this.scene.getNonDeletedElementsMap()),
|
|
2737
|
+
suggestedBindings: getSuggestedBindingsForArrows(selectedElements.filter((element) => element.id !== elbowArrow?.id || step !== 0), this.scene.getNonDeletedElementsMap(), this.state.zoom),
|
|
2742
2738
|
});
|
|
2739
|
+
this.scene.triggerUpdate();
|
|
2743
2740
|
event.preventDefault();
|
|
2744
2741
|
}
|
|
2745
2742
|
else if (event.key === KEYS.ENTER) {
|
|
@@ -2862,7 +2859,7 @@ class App extends React.Component {
|
|
|
2862
2859
|
}
|
|
2863
2860
|
if (event[KEYS.CTRL_OR_CMD] &&
|
|
2864
2861
|
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)) {
|
|
2865
|
-
|
|
2862
|
+
editorJotaiStore.set(activeConfirmDialogAtom, "clearCanvas");
|
|
2866
2863
|
}
|
|
2867
2864
|
// eye dropper
|
|
2868
2865
|
// -----------------------------------------------------------------------
|
|
@@ -2900,7 +2897,7 @@ class App extends React.Component {
|
|
|
2900
2897
|
this.setState({ isBindingEnabled: true });
|
|
2901
2898
|
}
|
|
2902
2899
|
if (isArrowKey(event.key)) {
|
|
2903
|
-
bindOrUnbindLinearElements(this.scene.getSelectedElements(this.state).filter(isLinearElement), this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements(), this.scene, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
|
|
2900
|
+
bindOrUnbindLinearElements(this.scene.getSelectedElements(this.state).filter(isLinearElement), this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements(), this.scene, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? [], this.state.zoom);
|
|
2904
2901
|
this.setState({ suggestedBindings: [] });
|
|
2905
2902
|
}
|
|
2906
2903
|
if (!event.altKey) {
|
|
@@ -2955,7 +2952,10 @@ class App extends React.Component {
|
|
|
2955
2952
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
|
|
2956
2953
|
}
|
|
2957
2954
|
else if (!isHoldingSpace) {
|
|
2958
|
-
setCursorForShape(this.interactiveCanvas,
|
|
2955
|
+
setCursorForShape(this.interactiveCanvas, {
|
|
2956
|
+
...this.state,
|
|
2957
|
+
activeTool: nextActiveTool,
|
|
2958
|
+
});
|
|
2959
2959
|
}
|
|
2960
2960
|
if (isToolIcon(document.activeElement)) {
|
|
2961
2961
|
this.focusContainer();
|
|
@@ -3258,7 +3258,11 @@ class App extends React.Component {
|
|
|
3258
3258
|
this.state.selectedElementIds[element.id] &&
|
|
3259
3259
|
shouldShowBoundingBox([element], this.state)) {
|
|
3260
3260
|
const selectionShape = getSelectionBoxShape(element, this.scene.getNonDeletedElementsMap(), isImageElement(element) ? 0 : this.getElementHitThreshold());
|
|
3261
|
-
|
|
3261
|
+
// if hitting the bounding box, return early
|
|
3262
|
+
// but if not, we should check for other cases as well (e.g. frame name)
|
|
3263
|
+
if (isPointInShape(pointFrom(x, y), selectionShape)) {
|
|
3264
|
+
return true;
|
|
3265
|
+
}
|
|
3262
3266
|
}
|
|
3263
3267
|
// take bound text element into consideration for hit collision as well
|
|
3264
3268
|
const hitBoundTextOfElement = hitElementBoundText(x, y, getBoundTextShape(element, this.scene.getNonDeletedElementsMap()));
|
|
@@ -3447,6 +3451,7 @@ class App extends React.Component {
|
|
|
3447
3451
|
return;
|
|
3448
3452
|
}
|
|
3449
3453
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
3454
|
+
let { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(event, this.state);
|
|
3450
3455
|
if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
|
|
3451
3456
|
if (event[KEYS.CTRL_OR_CMD] &&
|
|
3452
3457
|
(!this.state.editingLinearElement ||
|
|
@@ -3459,13 +3464,45 @@ class App extends React.Component {
|
|
|
3459
3464
|
});
|
|
3460
3465
|
return;
|
|
3461
3466
|
}
|
|
3467
|
+
else if (this.state.selectedLinearElement &&
|
|
3468
|
+
isElbowArrow(selectedElements[0])) {
|
|
3469
|
+
const hitCoords = LinearElementEditor.getSegmentMidpointHitCoords(this.state.selectedLinearElement, { x: sceneX, y: sceneY }, this.state, this.scene.getNonDeletedElementsMap());
|
|
3470
|
+
const midPoint = hitCoords
|
|
3471
|
+
? LinearElementEditor.getSegmentMidPointIndex(this.state.selectedLinearElement, this.state, hitCoords, this.scene.getNonDeletedElementsMap())
|
|
3472
|
+
: -1;
|
|
3473
|
+
if (midPoint && midPoint > -1) {
|
|
3474
|
+
this.store.shouldCaptureIncrement();
|
|
3475
|
+
LinearElementEditor.deleteFixedSegment(selectedElements[0], midPoint);
|
|
3476
|
+
const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords({
|
|
3477
|
+
...this.state.selectedLinearElement,
|
|
3478
|
+
segmentMidPointHoveredCoords: null,
|
|
3479
|
+
}, { x: sceneX, y: sceneY }, this.state, this.scene.getNonDeletedElementsMap());
|
|
3480
|
+
const nextIndex = nextCoords
|
|
3481
|
+
? LinearElementEditor.getSegmentMidPointIndex(this.state.selectedLinearElement, this.state, nextCoords, this.scene.getNonDeletedElementsMap())
|
|
3482
|
+
: null;
|
|
3483
|
+
this.setState({
|
|
3484
|
+
selectedLinearElement: {
|
|
3485
|
+
...this.state.selectedLinearElement,
|
|
3486
|
+
pointerDownState: {
|
|
3487
|
+
...this.state.selectedLinearElement.pointerDownState,
|
|
3488
|
+
segmentMidpoint: {
|
|
3489
|
+
index: nextIndex,
|
|
3490
|
+
value: hitCoords,
|
|
3491
|
+
added: false,
|
|
3492
|
+
},
|
|
3493
|
+
},
|
|
3494
|
+
segmentMidPointHoveredCoords: nextCoords,
|
|
3495
|
+
},
|
|
3496
|
+
});
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3462
3500
|
}
|
|
3463
3501
|
if (selectedElements.length === 1 && isImageElement(selectedElements[0])) {
|
|
3464
3502
|
this.startImageCropping(selectedElements[0]);
|
|
3465
3503
|
return;
|
|
3466
3504
|
}
|
|
3467
3505
|
resetCursor(this.interactiveCanvas);
|
|
3468
|
-
let { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(event, this.state);
|
|
3469
3506
|
const selectedGroupIds = getSelectedGroupIds(this.state);
|
|
3470
3507
|
if (selectedGroupIds.length > 0) {
|
|
3471
3508
|
const hitElement = this.getElementAtPosition(sceneX, sceneY);
|
|
@@ -3691,7 +3728,7 @@ class App extends React.Component {
|
|
|
3691
3728
|
});
|
|
3692
3729
|
}
|
|
3693
3730
|
if (editingLinearElement?.lastUncommittedPoint != null) {
|
|
3694
|
-
this.maybeSuggestBindingAtCursor(scenePointer);
|
|
3731
|
+
this.maybeSuggestBindingAtCursor(scenePointer, editingLinearElement.elbowed);
|
|
3695
3732
|
}
|
|
3696
3733
|
else {
|
|
3697
3734
|
// causes stack overflow if not sync
|
|
@@ -3708,7 +3745,7 @@ class App extends React.Component {
|
|
|
3708
3745
|
this.maybeSuggestBindingsForLinearElementAtCoords(newElement, [scenePointer], this.state.startBoundElement);
|
|
3709
3746
|
}
|
|
3710
3747
|
else {
|
|
3711
|
-
this.maybeSuggestBindingAtCursor(scenePointer);
|
|
3748
|
+
this.maybeSuggestBindingAtCursor(scenePointer, false);
|
|
3712
3749
|
}
|
|
3713
3750
|
}
|
|
3714
3751
|
if (this.state.multiElement) {
|
|
@@ -3760,24 +3797,15 @@ class App extends React.Component {
|
|
|
3760
3797
|
if (isPathALoop(points, this.state.zoom.value)) {
|
|
3761
3798
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
3762
3799
|
}
|
|
3763
|
-
|
|
3764
|
-
|
|
3800
|
+
// update last uncommitted point
|
|
3801
|
+
mutateElement(multiElement, {
|
|
3802
|
+
points: [
|
|
3765
3803
|
...points.slice(0, -1),
|
|
3766
3804
|
pointFrom(lastCommittedX + dxFromLastCommitted, lastCommittedY + dyFromLastCommitted),
|
|
3767
|
-
],
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
}
|
|
3772
|
-
else {
|
|
3773
|
-
// update last uncommitted point
|
|
3774
|
-
mutateElement(multiElement, {
|
|
3775
|
-
points: [
|
|
3776
|
-
...points.slice(0, -1),
|
|
3777
|
-
pointFrom(lastCommittedX + dxFromLastCommitted, lastCommittedY + dyFromLastCommitted),
|
|
3778
|
-
],
|
|
3779
|
-
}, false);
|
|
3780
|
-
}
|
|
3805
|
+
],
|
|
3806
|
+
}, false, {
|
|
3807
|
+
isDragging: true,
|
|
3808
|
+
});
|
|
3781
3809
|
// in this path, we're mutating multiElement to reflect
|
|
3782
3810
|
// how it will be after adding pointer position as the next point
|
|
3783
3811
|
// trigger update here so that new element canvas renders again to reflect this
|
|
@@ -3873,7 +3901,7 @@ class App extends React.Component {
|
|
|
3873
3901
|
activeEmbeddable: { element: hitElement, state: "hover" },
|
|
3874
3902
|
});
|
|
3875
3903
|
}
|
|
3876
|
-
else {
|
|
3904
|
+
else if (!hitElement || !isElbowArrow(hitElement)) {
|
|
3877
3905
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
|
3878
3906
|
if (this.state.activeEmbeddable?.state === "hover") {
|
|
3879
3907
|
this.setState({ activeEmbeddable: null });
|
|
@@ -4001,7 +4029,11 @@ class App extends React.Component {
|
|
|
4001
4029
|
hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(element, elementsMap, this.state.zoom, scenePointerX, scenePointerY);
|
|
4002
4030
|
segmentMidPointHoveredCoords =
|
|
4003
4031
|
LinearElementEditor.getSegmentMidpointHitCoords(linearElementEditor, { x: scenePointerX, y: scenePointerY }, this.state, this.scene.getNonDeletedElementsMap());
|
|
4004
|
-
|
|
4032
|
+
const isHoveringAPointHandle = isElbowArrow(element)
|
|
4033
|
+
? hoverPointIndex === 0 ||
|
|
4034
|
+
hoverPointIndex === element.points.length - 1
|
|
4035
|
+
: hoverPointIndex >= 0;
|
|
4036
|
+
if (isHoveringAPointHandle || segmentMidPointHoveredCoords) {
|
|
4005
4037
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
4006
4038
|
}
|
|
4007
4039
|
else if (this.hitElement(scenePointerX, scenePointerY, element)) {
|
|
@@ -4009,7 +4041,9 @@ class App extends React.Component {
|
|
|
4009
4041
|
}
|
|
4010
4042
|
}
|
|
4011
4043
|
else if (this.hitElement(scenePointerX, scenePointerY, element)) {
|
|
4012
|
-
if (
|
|
4044
|
+
if (
|
|
4045
|
+
// Ebow arrows can only be moved when unconnected
|
|
4046
|
+
!isElbowArrow(element) ||
|
|
4013
4047
|
!(element.startBinding || element.endBinding)) {
|
|
4014
4048
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
|
4015
4049
|
}
|
|
@@ -4036,6 +4070,12 @@ class App extends React.Component {
|
|
|
4036
4070
|
}
|
|
4037
4071
|
}
|
|
4038
4072
|
handleCanvasPointerDown = (event) => {
|
|
4073
|
+
const target = event.target;
|
|
4074
|
+
// capture subsequent pointer events to the canvas
|
|
4075
|
+
// this makes other elements non-interactive until pointer up
|
|
4076
|
+
if (target.setPointerCapture) {
|
|
4077
|
+
target.setPointerCapture(event.pointerId);
|
|
4078
|
+
}
|
|
4039
4079
|
this.maybeCleanupAfterMissingPointerUp(event.nativeEvent);
|
|
4040
4080
|
this.maybeUnfollowRemoteUser();
|
|
4041
4081
|
if (this.state.searchMatches) {
|
|
@@ -4045,7 +4085,7 @@ class App extends React.Component {
|
|
|
4045
4085
|
focus: false,
|
|
4046
4086
|
})),
|
|
4047
4087
|
}));
|
|
4048
|
-
|
|
4088
|
+
editorJotaiStore.set(searchItemInFocusAtom, null);
|
|
4049
4089
|
}
|
|
4050
4090
|
// since contextMenu options are potentially evaluated on each render,
|
|
4051
4091
|
// and an contextMenu action may depend on selection state, we must
|
|
@@ -4541,6 +4581,7 @@ class App extends React.Component {
|
|
|
4541
4581
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
4542
4582
|
if (selectedElements.length === 1 &&
|
|
4543
4583
|
!this.state.editingLinearElement &&
|
|
4584
|
+
!isElbowArrow(selectedElements[0]) &&
|
|
4544
4585
|
!(this.state.selectedLinearElement &&
|
|
4545
4586
|
this.state.selectedLinearElement.hoverPointIndex !== -1)) {
|
|
4546
4587
|
const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
|
|
@@ -4837,7 +4878,7 @@ class App extends React.Component {
|
|
|
4837
4878
|
selectedElementIds: makeNextSelectedElementIds(nextSelectedElementIds, prevState),
|
|
4838
4879
|
};
|
|
4839
4880
|
});
|
|
4840
|
-
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
|
|
4881
|
+
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), this.state.zoom);
|
|
4841
4882
|
this.setState({
|
|
4842
4883
|
newElement: element,
|
|
4843
4884
|
startBoundElement: boundElement,
|
|
@@ -5007,6 +5048,9 @@ class App extends React.Component {
|
|
|
5007
5048
|
locked: false,
|
|
5008
5049
|
frameId: topLayerFrame ? topLayerFrame.id : null,
|
|
5009
5050
|
elbowed: this.state.currentItemArrowType === ARROW_TYPE.elbow,
|
|
5051
|
+
fixedSegments: this.state.currentItemArrowType === ARROW_TYPE.elbow
|
|
5052
|
+
? []
|
|
5053
|
+
: null,
|
|
5010
5054
|
})
|
|
5011
5055
|
: newLinearElement({
|
|
5012
5056
|
type: elementType,
|
|
@@ -5037,7 +5081,7 @@ class App extends React.Component {
|
|
|
5037
5081
|
mutateElement(element, {
|
|
5038
5082
|
points: [...element.points, pointFrom(0, 0)],
|
|
5039
5083
|
});
|
|
5040
|
-
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), isElbowArrow(element));
|
|
5084
|
+
const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), this.state.zoom, isElbowArrow(element), isElbowArrow(element));
|
|
5041
5085
|
this.scene.insertElement(element);
|
|
5042
5086
|
this.setState({
|
|
5043
5087
|
newElement: element,
|
|
@@ -5167,6 +5211,35 @@ class App extends React.Component {
|
|
|
5167
5211
|
return;
|
|
5168
5212
|
}
|
|
5169
5213
|
const pointerCoords = viewportCoordsToSceneCoords(event, this.state);
|
|
5214
|
+
if (this.state.selectedLinearElement &&
|
|
5215
|
+
this.state.selectedLinearElement.elbowed &&
|
|
5216
|
+
this.state.selectedLinearElement.pointerDownState.segmentMidpoint.index) {
|
|
5217
|
+
const [gridX, gridY] = getGridPoint(pointerCoords.x, pointerCoords.y, event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize());
|
|
5218
|
+
let index = this.state.selectedLinearElement.pointerDownState.segmentMidpoint
|
|
5219
|
+
.index;
|
|
5220
|
+
if (index < 0) {
|
|
5221
|
+
const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords({
|
|
5222
|
+
...this.state.selectedLinearElement,
|
|
5223
|
+
segmentMidPointHoveredCoords: null,
|
|
5224
|
+
}, { x: gridX, y: gridY }, this.state, this.scene.getNonDeletedElementsMap());
|
|
5225
|
+
index = nextCoords
|
|
5226
|
+
? LinearElementEditor.getSegmentMidPointIndex(this.state.selectedLinearElement, this.state, nextCoords, this.scene.getNonDeletedElementsMap())
|
|
5227
|
+
: -1;
|
|
5228
|
+
}
|
|
5229
|
+
const ret = LinearElementEditor.moveFixedSegment(this.state.selectedLinearElement, index, gridX, gridY, this.scene.getNonDeletedElementsMap());
|
|
5230
|
+
flushSync(() => {
|
|
5231
|
+
if (this.state.selectedLinearElement) {
|
|
5232
|
+
this.setState({
|
|
5233
|
+
selectedLinearElement: {
|
|
5234
|
+
...this.state.selectedLinearElement,
|
|
5235
|
+
segmentMidPointHoveredCoords: ret.segmentMidPointHoveredCoords,
|
|
5236
|
+
pointerDownState: ret.pointerDownState,
|
|
5237
|
+
},
|
|
5238
|
+
});
|
|
5239
|
+
}
|
|
5240
|
+
});
|
|
5241
|
+
return;
|
|
5242
|
+
}
|
|
5170
5243
|
const lastPointerCoords = this.lastPointerMoveCoords ?? pointerDownState.origin;
|
|
5171
5244
|
this.lastPointerMoveCoords = pointerCoords;
|
|
5172
5245
|
// We need to initialize dragOffsetXY only after we've updated
|
|
@@ -5370,13 +5443,12 @@ class App extends React.Component {
|
|
|
5370
5443
|
this.maybeCacheVisibleGaps(event, selectedElements);
|
|
5371
5444
|
this.maybeCacheReferenceSnapPoints(event, selectedElements);
|
|
5372
5445
|
const { snapOffset, snapLines } = snapDraggedElements(originalElements, dragOffset, this, event, this.scene.getNonDeletedElementsMap());
|
|
5373
|
-
|
|
5374
|
-
this.setState({ snapLines });
|
|
5375
|
-
});
|
|
5446
|
+
this.setState({ snapLines });
|
|
5376
5447
|
// when we're editing the name of a frame, we want the user to be
|
|
5377
5448
|
// able to select and interact with the text input
|
|
5378
|
-
!this.state.editingFrame
|
|
5449
|
+
if (!this.state.editingFrame) {
|
|
5379
5450
|
dragSelectedElements(pointerDownState, selectedElements, dragOffset, this.scene, snapOffset, event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize());
|
|
5451
|
+
}
|
|
5380
5452
|
this.setState({
|
|
5381
5453
|
selectedElementsAreBeingDragged: true,
|
|
5382
5454
|
// element is being dragged and selectionElement that was created on pointer down
|
|
@@ -5386,7 +5458,7 @@ class App extends React.Component {
|
|
|
5386
5458
|
if (selectedElements.length !== 1 ||
|
|
5387
5459
|
!isElbowArrow(selectedElements[0])) {
|
|
5388
5460
|
this.setState({
|
|
5389
|
-
suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap()),
|
|
5461
|
+
suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap(), this.state.zoom),
|
|
5390
5462
|
});
|
|
5391
5463
|
}
|
|
5392
5464
|
// We duplicate the selected element if alt is pressed on pointer move
|
|
@@ -5431,7 +5503,12 @@ class App extends React.Component {
|
|
|
5431
5503
|
nextElements.push(element);
|
|
5432
5504
|
}
|
|
5433
5505
|
}
|
|
5434
|
-
|
|
5506
|
+
let nextSceneElements = [
|
|
5507
|
+
...nextElements,
|
|
5508
|
+
...elementsToAppend,
|
|
5509
|
+
];
|
|
5510
|
+
const mappedNewSceneElements = this.props.onDuplicate?.(nextSceneElements, elements);
|
|
5511
|
+
nextSceneElements = mappedNewSceneElements || nextSceneElements;
|
|
5435
5512
|
syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend));
|
|
5436
5513
|
bindTextToShapeAfterDuplication(nextElements, elementsToAppend, oldIdToDuplicatedId);
|
|
5437
5514
|
fixBindingsAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId, "duplicatesServeAsOld");
|
|
@@ -5487,16 +5564,11 @@ class App extends React.Component {
|
|
|
5487
5564
|
points: [...points, pointFrom(dx, dy)],
|
|
5488
5565
|
}, false);
|
|
5489
5566
|
}
|
|
5490
|
-
else if (points.length
|
|
5491
|
-
|
|
5492
|
-
isDragging: true,
|
|
5493
|
-
informMutation: false,
|
|
5494
|
-
});
|
|
5495
|
-
}
|
|
5496
|
-
else if (points.length === 2) {
|
|
5567
|
+
else if (points.length === 2 ||
|
|
5568
|
+
(points.length > 1 && isElbowArrow(newElement))) {
|
|
5497
5569
|
mutateElement(newElement, {
|
|
5498
5570
|
points: [...points.slice(0, -1), pointFrom(dx, dy)],
|
|
5499
|
-
}, false);
|
|
5571
|
+
}, false, { isDragging: true });
|
|
5500
5572
|
}
|
|
5501
5573
|
this.setState({
|
|
5502
5574
|
newElement,
|
|
@@ -5537,7 +5609,7 @@ class App extends React.Component {
|
|
|
5537
5609
|
}
|
|
5538
5610
|
}
|
|
5539
5611
|
const elementsWithinSelection = this.state.selectionElement
|
|
5540
|
-
? getElementsWithinSelection(elements, this.state.selectionElement, this.scene.getNonDeletedElementsMap())
|
|
5612
|
+
? getElementsWithinSelection(elements, this.state.selectionElement, this.scene.getNonDeletedElementsMap(), false)
|
|
5541
5613
|
: [];
|
|
5542
5614
|
this.setState((prevState) => {
|
|
5543
5615
|
const nextSelectedElementIds = {
|
|
@@ -5630,6 +5702,20 @@ class App extends React.Component {
|
|
|
5630
5702
|
selectedElementsAreBeingDragged: false,
|
|
5631
5703
|
});
|
|
5632
5704
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
5705
|
+
if (pointerDownState.drag.hasOccurred &&
|
|
5706
|
+
pointerDownState.hit?.element?.id) {
|
|
5707
|
+
const element = elementsMap.get(pointerDownState.hit.element.id);
|
|
5708
|
+
if (isBindableElement(element)) {
|
|
5709
|
+
// Renormalize elbow arrows when they are changed via indirect move
|
|
5710
|
+
element.boundElements
|
|
5711
|
+
?.filter((e) => e.type === "arrow")
|
|
5712
|
+
.map((e) => elementsMap.get(e.id))
|
|
5713
|
+
.filter((e) => isElbowArrow(e))
|
|
5714
|
+
.forEach((e) => {
|
|
5715
|
+
!!e && mutateElement(e, {}, true);
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5718
|
+
}
|
|
5633
5719
|
// Handle end of dragging a point of a linear element, might close a loop
|
|
5634
5720
|
// and sets binding element
|
|
5635
5721
|
if (this.state.editingLinearElement) {
|
|
@@ -5649,6 +5735,13 @@ class App extends React.Component {
|
|
|
5649
5735
|
}
|
|
5650
5736
|
}
|
|
5651
5737
|
else if (this.state.selectedLinearElement) {
|
|
5738
|
+
// Normalize elbow arrow points, remove close parallel segments
|
|
5739
|
+
if (this.state.selectedLinearElement.elbowed) {
|
|
5740
|
+
const element = LinearElementEditor.getElement(this.state.selectedLinearElement.elementId, this.scene.getNonDeletedElementsMap());
|
|
5741
|
+
if (element) {
|
|
5742
|
+
mutateElement(element, {}, true);
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5652
5745
|
if (pointerDownState.hit?.element?.id !==
|
|
5653
5746
|
this.state.selectedLinearElement.elementId) {
|
|
5654
5747
|
const selectedELements = this.scene.getSelectedElements(this.state);
|
|
@@ -5805,7 +5898,7 @@ class App extends React.Component {
|
|
|
5805
5898
|
}
|
|
5806
5899
|
if (isFrameLikeElement(newElement)) {
|
|
5807
5900
|
const elementsInsideFrame = getElementsInNewFrame(this.scene.getElementsIncludingDeleted(), newElement, this.scene.getNonDeletedElementsMap());
|
|
5808
|
-
this.scene.replaceAllElements(addElementsToFrame(this.scene.getElementsMapIncludingDeleted(), elementsInsideFrame, newElement));
|
|
5901
|
+
this.scene.replaceAllElements(addElementsToFrame(this.scene.getElementsMapIncludingDeleted(), elementsInsideFrame, newElement, this.state));
|
|
5809
5902
|
}
|
|
5810
5903
|
if (newElement) {
|
|
5811
5904
|
mutateElement(newElement, getNormalizedDimensions(newElement));
|
|
@@ -5868,7 +5961,7 @@ class App extends React.Component {
|
|
|
5868
5961
|
if (this.state.editingGroupId) {
|
|
5869
5962
|
updateGroupIdsAfterEditingGroup(elementsToAdd);
|
|
5870
5963
|
}
|
|
5871
|
-
nextElements = addElementsToFrame(nextElements, elementsToAdd, topLayerFrame);
|
|
5964
|
+
nextElements = addElementsToFrame(nextElements, elementsToAdd, topLayerFrame, this.state);
|
|
5872
5965
|
}
|
|
5873
5966
|
else if (!topLayerFrame) {
|
|
5874
5967
|
if (this.state.editingGroupId) {
|
|
@@ -5909,10 +6002,10 @@ class App extends React.Component {
|
|
|
5909
6002
|
const hitElement = pointerDownState.hit.element;
|
|
5910
6003
|
if (this.state.selectedLinearElement?.elementId !== hitElement?.id &&
|
|
5911
6004
|
isLinearElement(hitElement)) {
|
|
5912
|
-
const
|
|
6005
|
+
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
5913
6006
|
// set selectedLinearElement when no other element selected except
|
|
5914
6007
|
// the one we've hit
|
|
5915
|
-
if (
|
|
6008
|
+
if (selectedElements.length === 1) {
|
|
5916
6009
|
this.setState({
|
|
5917
6010
|
selectedLinearElement: new LinearElementEditor(hitElement),
|
|
5918
6011
|
});
|
|
@@ -6059,8 +6152,10 @@ class App extends React.Component {
|
|
|
6059
6152
|
}
|
|
6060
6153
|
}
|
|
6061
6154
|
if (
|
|
6062
|
-
// not dragged
|
|
6063
|
-
!
|
|
6155
|
+
// not elbow midpoint dragged
|
|
6156
|
+
!(hitElement && isElbowArrow(hitElement)) &&
|
|
6157
|
+
// not dragged
|
|
6158
|
+
!pointerDownState.drag.hasOccurred &&
|
|
6064
6159
|
// not resized
|
|
6065
6160
|
!this.state.isResizing &&
|
|
6066
6161
|
// only hitting the bounding box of the previous hit element
|
|
@@ -6118,7 +6213,7 @@ class App extends React.Component {
|
|
|
6118
6213
|
const linearElements = this.scene
|
|
6119
6214
|
.getSelectedElements(this.state)
|
|
6120
6215
|
.filter(isLinearElement);
|
|
6121
|
-
bindOrUnbindLinearElements(linearElements, this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements(), this.scene, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
|
|
6216
|
+
bindOrUnbindLinearElements(linearElements, this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements(), this.scene, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? [], this.state.zoom);
|
|
6122
6217
|
}
|
|
6123
6218
|
if (activeTool.type === "laser") {
|
|
6124
6219
|
this.laserTrails.endPath();
|
|
@@ -6465,8 +6560,8 @@ class App extends React.Component {
|
|
|
6465
6560
|
this.setState({ isBindingEnabled: shouldEnableBinding });
|
|
6466
6561
|
}
|
|
6467
6562
|
};
|
|
6468
|
-
maybeSuggestBindingAtCursor = (pointerCoords) => {
|
|
6469
|
-
const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
|
|
6563
|
+
maybeSuggestBindingAtCursor = (pointerCoords, considerAll) => {
|
|
6564
|
+
const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), this.state.zoom, false, considerAll);
|
|
6470
6565
|
this.setState({
|
|
6471
6566
|
suggestedBindings: hoveredBindableElement != null ? [hoveredBindableElement] : [],
|
|
6472
6567
|
});
|
|
@@ -6481,7 +6576,7 @@ class App extends React.Component {
|
|
|
6481
6576
|
return;
|
|
6482
6577
|
}
|
|
6483
6578
|
const suggestedBindings = pointerCoords.reduce((acc, coords) => {
|
|
6484
|
-
const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(),
|
|
6579
|
+
const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), this.state.zoom, isElbowArrow(linearElement), isElbowArrow(linearElement));
|
|
6485
6580
|
if (hoveredBindableElement != null &&
|
|
6486
6581
|
!isLinearElementSimpleAndAlreadyBound(linearElement, oppositeBindingBoundElement?.id, hoveredBindableElement)) {
|
|
6487
6582
|
acc.push(hoveredBindableElement);
|
|
@@ -6798,7 +6893,7 @@ class App extends React.Component {
|
|
|
6798
6893
|
}
|
|
6799
6894
|
const transformHandleType = pointerDownState.resize.handleType;
|
|
6800
6895
|
const pointerCoords = pointerDownState.lastCoords;
|
|
6801
|
-
const [x, y] = getGridPoint(pointerCoords.x - pointerDownState.resize.offset.x, pointerCoords.y - pointerDownState.resize.offset.y, this.getEffectiveGridSize());
|
|
6896
|
+
const [x, y] = getGridPoint(pointerCoords.x - pointerDownState.resize.offset.x, pointerCoords.y - pointerDownState.resize.offset.y, event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize());
|
|
6802
6897
|
const croppingElement = this.scene
|
|
6803
6898
|
.getNonDeletedElementsMap()
|
|
6804
6899
|
.get(this.state.croppingElementId);
|
|
@@ -6812,7 +6907,14 @@ class App extends React.Component {
|
|
|
6812
6907
|
isImageElement(croppingAtStateStart) &&
|
|
6813
6908
|
image &&
|
|
6814
6909
|
!(image instanceof Promise)) {
|
|
6815
|
-
|
|
6910
|
+
const [gridX, gridY] = getGridPoint(pointerCoords.x, pointerCoords.y, event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize());
|
|
6911
|
+
const dragOffset = {
|
|
6912
|
+
x: gridX - pointerDownState.originInGrid.x,
|
|
6913
|
+
y: gridY - pointerDownState.originInGrid.y,
|
|
6914
|
+
};
|
|
6915
|
+
this.maybeCacheReferenceSnapPoints(event, [croppingElement]);
|
|
6916
|
+
const { snapOffset, snapLines } = snapResizingElements([croppingElement], [croppingAtStateStart], this, event, dragOffset, transformHandleType);
|
|
6917
|
+
mutateElement(croppingElement, cropElement(croppingElement, transformHandleType, image.naturalWidth, image.naturalHeight, x + snapOffset.x, y + snapOffset.y, event.shiftKey
|
|
6816
6918
|
? croppingAtStateStart.width / croppingAtStateStart.height
|
|
6817
6919
|
: undefined));
|
|
6818
6920
|
updateBoundElements(croppingElement, this.scene.getNonDeletedElementsMap(), {
|
|
@@ -6823,6 +6925,7 @@ class App extends React.Component {
|
|
|
6823
6925
|
});
|
|
6824
6926
|
this.setState({
|
|
6825
6927
|
isCropping: transformHandleType && transformHandleType !== "rotation",
|
|
6928
|
+
snapLines,
|
|
6826
6929
|
});
|
|
6827
6930
|
}
|
|
6828
6931
|
return true;
|
|
@@ -6879,10 +6982,10 @@ class App extends React.Component {
|
|
|
6879
6982
|
snapLines,
|
|
6880
6983
|
});
|
|
6881
6984
|
}
|
|
6882
|
-
if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.some((element) => isImageElement(element))
|
|
6985
|
+
if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), this.scene, shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.some((element) => isImageElement(element))
|
|
6883
6986
|
? !shouldMaintainAspectRatio(event)
|
|
6884
6987
|
: shouldMaintainAspectRatio(event), resizeX, resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y)) {
|
|
6885
|
-
const suggestedBindings = getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap());
|
|
6988
|
+
const suggestedBindings = getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap(), this.state.zoom);
|
|
6886
6989
|
const elementsToHighlight = new Set();
|
|
6887
6990
|
selectedFrames.forEach((frame) => {
|
|
6888
6991
|
getElementsInResizingFrame(this.scene.getNonDeletedElements(), frame, this.state, this.scene.getNonDeletedElementsMap()).forEach((element) => elementsToHighlight.add(element));
|
|
@@ -6938,8 +7041,10 @@ class App extends React.Component {
|
|
|
6938
7041
|
actionCut,
|
|
6939
7042
|
actionCopy,
|
|
6940
7043
|
actionPaste,
|
|
7044
|
+
CONTEXT_MENU_SEPARATOR,
|
|
6941
7045
|
actionSelectAllElementsInFrame,
|
|
6942
7046
|
actionRemoveAllElementsFromFrame,
|
|
7047
|
+
actionWrapSelectionInFrame,
|
|
6943
7048
|
CONTEXT_MENU_SEPARATOR,
|
|
6944
7049
|
actionToggleCropEditor,
|
|
6945
7050
|
CONTEXT_MENU_SEPARATOR,
|