@excalidraw/excalidraw 0.17.1-d2f67e6 → 0.17.1-e63dd02
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 +4 -0
- package/dist/browser/dev/excalidraw-assets-dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-EM6LVGFW.js → chunk-IT7T3AIK.js} +23 -5
- package/dist/browser/dev/excalidraw-assets-dev/chunk-IT7T3AIK.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-B4UMSLQQ.js → chunk-RNHSD5AR.js} +7451 -2098
- package/dist/browser/dev/excalidraw-assets-dev/chunk-RNHSD5AR.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{dist-6QVAH5JA.js → dist-DNSPZDOZ.js} +31 -19
- package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js → en-XV7OZCPP.js} +6 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js → image-77HZYGLG.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css → image-WDHYGKKP.css} +1 -1
- package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css.map → image-WDHYGKKP.css.map} +2 -2
- package/dist/browser/dev/index.css +449 -114
- package/dist/browser/dev/index.css.map +3 -3
- package/dist/browser/dev/index.js +4143 -5956
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/chunk-OYEADJSR.js +63 -0
- package/dist/browser/prod/excalidraw-assets/{chunk-7DXALCB2.js → chunk-PDYFZJMS.js} +3 -3
- package/dist/browser/prod/excalidraw-assets/dist-NLUQPPQQ.js +7 -0
- package/dist/browser/prod/excalidraw-assets/en-YVAVVILW.js +1 -0
- package/dist/browser/prod/excalidraw-assets/image-X3GFZHNN.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +40 -50
- package/dist/dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/dev/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
- package/dist/dev/index.css +449 -114
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +21626 -18122
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionAddToLibrary.d.ts +9 -3
- package/dist/excalidraw/actions/actionBoundText.d.ts +6 -2
- package/dist/excalidraw/actions/actionCanvas.d.ts +36 -12
- package/dist/excalidraw/actions/actionClipboard.d.ts +22 -7
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +12 -5
- package/dist/excalidraw/actions/actionDeleteSelected.js +24 -5
- package/dist/excalidraw/actions/actionDuplicateSelection.js +1 -2
- package/dist/excalidraw/actions/actionElementLock.d.ts +6 -2
- package/dist/excalidraw/actions/actionExport.d.ts +27 -9
- package/dist/excalidraw/actions/actionFinalize.d.ts +6 -2
- package/dist/excalidraw/actions/actionFinalize.js +2 -2
- package/dist/excalidraw/actions/actionFlip.js +2 -2
- package/dist/excalidraw/actions/actionFrame.d.ts +12 -4
- package/dist/excalidraw/actions/actionGroup.d.ts +6 -2
- package/dist/excalidraw/actions/actionHistory.js +4 -4
- package/dist/excalidraw/actions/actionLinearEditor.d.ts +3 -1
- package/dist/excalidraw/actions/actionLinearEditor.js +3 -2
- package/dist/excalidraw/actions/actionLink.d.ts +3 -1
- package/dist/excalidraw/actions/actionMenu.d.ts +9 -3
- package/dist/excalidraw/actions/actionNavigate.d.ts +6 -2
- package/dist/excalidraw/actions/actionProperties.d.ts +411 -56
- package/dist/excalidraw/actions/actionProperties.js +383 -58
- package/dist/excalidraw/actions/actionSelectAll.d.ts +3 -1
- package/dist/excalidraw/actions/actionStyles.d.ts +3 -1
- package/dist/excalidraw/actions/actionStyles.js +3 -2
- package/dist/excalidraw/actions/actionToggleGridMode.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleStats.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleViewMode.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleZenMode.d.ts +3 -1
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/analytics.js +9 -7
- package/dist/excalidraw/appState.d.ts +1 -0
- package/dist/excalidraw/appState.js +9 -1
- package/dist/excalidraw/binaryheap.d.ts +12 -0
- package/dist/excalidraw/binaryheap.js +93 -0
- package/dist/excalidraw/change.d.ts +2 -1
- package/dist/excalidraw/change.js +6 -4
- package/dist/excalidraw/charts.js +0 -10
- package/dist/excalidraw/components/Actions.js +7 -5
- package/dist/excalidraw/components/App.d.ts +5 -9
- package/dist/excalidraw/components/App.js +218 -161
- package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
- package/dist/excalidraw/components/ButtonIcon.js +8 -0
- package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
- package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
- package/dist/excalidraw/components/ButtonSeparator.js +7 -0
- package/dist/excalidraw/components/ColorPicker/ColorPicker.js +47 -79
- package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
- package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
- package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
- package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
- package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
- package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
- package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
- package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
- package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
- package/dist/excalidraw/components/HelpDialog.js +1 -1
- package/dist/excalidraw/components/HintViewer.js +6 -3
- package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
- package/dist/excalidraw/components/PropertiesPopover.js +31 -0
- package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
- package/dist/excalidraw/components/QuickSearch.js +8 -0
- package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
- package/dist/excalidraw/components/ScrollableList.js +8 -0
- package/dist/excalidraw/components/Stats/Angle.d.ts +7 -3
- package/dist/excalidraw/components/Stats/Angle.js +39 -31
- package/dist/excalidraw/components/Stats/Dimension.d.ts +6 -3
- package/dist/excalidraw/components/Stats/Dimension.js +51 -49
- package/dist/excalidraw/components/Stats/DragInput.d.ts +15 -6
- package/dist/excalidraw/components/Stats/DragInput.js +59 -26
- package/dist/excalidraw/components/Stats/FontSize.d.ts +8 -4
- package/dist/excalidraw/components/Stats/FontSize.js +39 -36
- package/dist/excalidraw/components/Stats/MultiAngle.d.ts +5 -3
- package/dist/excalidraw/components/Stats/MultiAngle.js +43 -34
- package/dist/excalidraw/components/Stats/MultiDimension.d.ts +5 -3
- package/dist/excalidraw/components/Stats/MultiDimension.js +101 -99
- package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +6 -3
- package/dist/excalidraw/components/Stats/MultiFontSize.js +47 -32
- package/dist/excalidraw/components/Stats/MultiPosition.d.ts +3 -1
- package/dist/excalidraw/components/Stats/MultiPosition.js +52 -48
- package/dist/excalidraw/components/Stats/Position.d.ts +5 -1
- package/dist/excalidraw/components/Stats/Position.js +31 -29
- package/dist/excalidraw/components/Stats/index.js +5 -17
- package/dist/excalidraw/components/Stats/utils.d.ts +14 -3
- package/dist/excalidraw/components/Stats/utils.js +48 -9
- package/dist/excalidraw/components/TTDDialog/common.d.ts +2 -2
- package/dist/excalidraw/components/TTDDialog/common.js +3 -7
- package/dist/excalidraw/components/UserList.js +22 -22
- package/dist/excalidraw/components/canvases/StaticCanvas.js +1 -0
- package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
- package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
- package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
- package/dist/excalidraw/components/icons.d.ts +4 -0
- package/dist/excalidraw/components/icons.js +7 -0
- package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
- package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
- package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
- package/dist/excalidraw/constants.d.ts +17 -2
- package/dist/excalidraw/constants.js +21 -4
- package/dist/excalidraw/data/reconcile.js +18 -1
- package/dist/excalidraw/data/restore.js +55 -9
- package/dist/excalidraw/data/transform.js +8 -5
- package/dist/excalidraw/element/binding.d.ts +28 -9
- package/dist/excalidraw/element/binding.js +303 -71
- package/dist/excalidraw/element/collision.d.ts +1 -1
- package/dist/excalidraw/element/collision.js +4 -1
- package/dist/excalidraw/element/dragElements.d.ts +2 -2
- package/dist/excalidraw/element/dragElements.js +13 -3
- package/dist/excalidraw/element/embeddable.d.ts +3 -1
- package/dist/excalidraw/element/heading.d.ts +11 -0
- package/dist/excalidraw/element/heading.js +81 -0
- package/dist/excalidraw/element/index.d.ts +1 -1
- package/dist/excalidraw/element/index.js +1 -1
- package/dist/excalidraw/element/linearElementEditor.d.ts +21 -13
- package/dist/excalidraw/element/linearElementEditor.js +133 -56
- package/dist/excalidraw/element/newElement.d.ts +8 -3
- package/dist/excalidraw/element/newElement.js +15 -2
- package/dist/excalidraw/element/resizeElements.d.ts +4 -3
- package/dist/excalidraw/element/resizeElements.js +47 -23
- package/dist/excalidraw/element/routing.d.ts +13 -0
- package/dist/excalidraw/element/routing.js +641 -0
- package/dist/excalidraw/element/textElement.d.ts +3 -26
- package/dist/excalidraw/element/textElement.js +54 -110
- package/dist/excalidraw/element/textWysiwyg.js +39 -47
- package/dist/excalidraw/element/transformHandles.js +7 -2
- package/dist/excalidraw/element/typeChecks.d.ts +5 -2
- package/dist/excalidraw/element/typeChecks.js +17 -0
- package/dist/excalidraw/element/types.d.ts +12 -1
- package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +21 -0
- package/dist/excalidraw/fonts/ExcalidrawFont.js +112 -0
- package/dist/excalidraw/fonts/index.d.ts +58 -0
- package/dist/excalidraw/fonts/index.js +240 -0
- package/dist/excalidraw/fonts/metadata.d.ts +36 -0
- package/dist/excalidraw/fonts/metadata.js +91 -0
- package/dist/excalidraw/fractionalIndex.d.ts +11 -4
- package/dist/excalidraw/fractionalIndex.js +38 -6
- package/dist/excalidraw/frame.d.ts +1 -1
- package/dist/excalidraw/frame.js +3 -3
- package/dist/excalidraw/history.d.ts +4 -3
- package/dist/excalidraw/history.js +8 -8
- package/dist/excalidraw/index.d.ts +1 -1
- package/dist/excalidraw/index.js +3 -3
- package/dist/excalidraw/locales/en.json +18 -4
- package/dist/excalidraw/math.d.ts +43 -0
- package/dist/excalidraw/math.js +110 -0
- package/dist/excalidraw/mermaid.js +4 -3
- package/dist/excalidraw/renderer/interactiveScene.js +33 -17
- package/dist/excalidraw/renderer/renderElement.d.ts +2 -0
- package/dist/excalidraw/renderer/renderElement.js +74 -54
- package/dist/excalidraw/renderer/staticSvgScene.js +2 -1
- package/dist/excalidraw/scene/Scene.js +9 -3
- package/dist/excalidraw/scene/Shape.js +56 -5
- package/dist/excalidraw/scene/comparisons.d.ts +1 -0
- package/dist/excalidraw/scene/comparisons.js +1 -1
- package/dist/excalidraw/scene/export.d.ts +2 -1
- package/dist/excalidraw/scene/export.js +33 -35
- package/dist/excalidraw/scene/types.d.ts +1 -4
- package/dist/excalidraw/shapes.d.ts +8 -0
- package/dist/excalidraw/shapes.js +57 -0
- package/dist/excalidraw/types.d.ts +8 -3
- package/dist/excalidraw/utils.d.ts +11 -1
- package/dist/excalidraw/utils.js +22 -0
- package/dist/prod/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/prod/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +49 -53
- package/dist/utils/export.d.ts +2 -1
- package/dist/utils/export.js +2 -1
- package/dist/utils/geometry/geometry.d.ts +2 -1
- package/dist/utils/geometry/geometry.js +5 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/history.ts +9 -2
- package/package.json +2 -2
- package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/chunk-B4UMSLQQ.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-EM6LVGFW.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +0 -7
- package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/chunk-EGOLGOLD.js +0 -55
- package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +0 -7
- package/dist/browser/prod/excalidraw-assets/en-6E7MYLWO.js +0 -1
- package/dist/browser/prod/excalidraw-assets/image-SI7BKULC.js +0 -1
- package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
- package/dist/excalidraw/scene/Fonts.d.ts +0 -19
- package/dist/excalidraw/scene/Fonts.js +0 -66
- package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
- /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
- /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
- /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
- /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js.map → en-XV7OZCPP.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js.map → image-77HZYGLG.js.map} +0 -0
- /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
- /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
- /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
- /package/dist/browser/prod/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
|
@@ -6,19 +6,22 @@ import * as GATransform from "../gatransforms";
|
|
|
6
6
|
import { getElementAbsoluteCoords } from "./bounds";
|
|
7
7
|
import { isPointOnShape } from "../../utils/collision";
|
|
8
8
|
import { getElementAtPosition } from "../scene";
|
|
9
|
-
import { isArrowElement, isBindableElement, isBindingElement, isBoundToContainer, isLinearElement, isTextElement, } from "./typeChecks";
|
|
9
|
+
import { isArrowElement, isBindableElement, isBindingElement, isBoundToContainer, isElbowArrow, isFrameLikeElement, isLinearElement, isTextElement, } from "./typeChecks";
|
|
10
10
|
import { mutateElement } from "./mutateElement";
|
|
11
|
-
import Scene from "../scene/Scene";
|
|
12
11
|
import { LinearElementEditor } from "./linearElementEditor";
|
|
13
12
|
import { arrayToMap, tupleToCoors } from "../utils";
|
|
14
13
|
import { KEYS } from "../keys";
|
|
15
14
|
import { getBoundTextElement, handleBindTextResize } from "./textElement";
|
|
15
|
+
import { getElementShape } from "../shapes";
|
|
16
|
+
import { aabbForElement, clamp, distanceSq2d, getCenterForBounds, getCenterForElement, pointInsideBounds, pointToVector, rotatePoint, } from "../math";
|
|
17
|
+
import { compareHeading, HEADING_DOWN, HEADING_LEFT, HEADING_RIGHT, HEADING_UP, headingForPointFromElement, vectorToHeading, } from "./heading";
|
|
16
18
|
export const shouldEnableBindingForPointerEvent = (event) => {
|
|
17
19
|
return !event[KEYS.CTRL_OR_CMD];
|
|
18
20
|
};
|
|
19
21
|
export const isBindingEnabled = (appState) => {
|
|
20
22
|
return appState.isBindingEnabled;
|
|
21
23
|
};
|
|
24
|
+
export const FIXED_BINDING_DISTANCE = 5;
|
|
22
25
|
const getNonDeletedElements = (scene, ids) => {
|
|
23
26
|
const result = [];
|
|
24
27
|
ids.forEach((id) => {
|
|
@@ -29,13 +32,13 @@ const getNonDeletedElements = (scene, ids) => {
|
|
|
29
32
|
});
|
|
30
33
|
return result;
|
|
31
34
|
};
|
|
32
|
-
export const bindOrUnbindLinearElement = (linearElement, startBindingElement, endBindingElement, elementsMap) => {
|
|
35
|
+
export const bindOrUnbindLinearElement = (linearElement, startBindingElement, endBindingElement, elementsMap, scene) => {
|
|
33
36
|
const boundToElementIds = new Set();
|
|
34
37
|
const unboundFromElementIds = new Set();
|
|
35
|
-
bindOrUnbindLinearElementEdge(linearElement, startBindingElement, endBindingElement, "start", boundToElementIds, unboundFromElementIds, elementsMap);
|
|
36
|
-
bindOrUnbindLinearElementEdge(linearElement, endBindingElement, startBindingElement, "end", boundToElementIds, unboundFromElementIds, elementsMap);
|
|
38
|
+
bindOrUnbindLinearElementEdge(linearElement, startBindingElement, endBindingElement, "start", boundToElementIds, unboundFromElementIds, elementsMap, scene);
|
|
39
|
+
bindOrUnbindLinearElementEdge(linearElement, endBindingElement, startBindingElement, "end", boundToElementIds, unboundFromElementIds, elementsMap, scene);
|
|
37
40
|
const onlyUnbound = Array.from(unboundFromElementIds).filter((id) => !boundToElementIds.has(id));
|
|
38
|
-
getNonDeletedElements(
|
|
41
|
+
getNonDeletedElements(scene, onlyUnbound).forEach((element) => {
|
|
39
42
|
mutateElement(element, {
|
|
40
43
|
boundElements: element.boundElements?.filter((element) => element.type !== "arrow" || element.id !== linearElement.id),
|
|
41
44
|
});
|
|
@@ -45,7 +48,7 @@ const bindOrUnbindLinearElementEdge = (linearElement, bindableElement, otherEdge
|
|
|
45
48
|
// Is mutated
|
|
46
49
|
boundToElementIds,
|
|
47
50
|
// Is mutated
|
|
48
|
-
unboundFromElementIds, elementsMap) => {
|
|
51
|
+
unboundFromElementIds, elementsMap, scene) => {
|
|
49
52
|
// "keep" is for method chaining convenience, a "no-op", so just bail out
|
|
50
53
|
if (bindableElement === "keep") {
|
|
51
54
|
return;
|
|
@@ -77,72 +80,72 @@ unboundFromElementIds, elementsMap) => {
|
|
|
77
80
|
boundToElementIds.add(bindableElement.id);
|
|
78
81
|
}
|
|
79
82
|
};
|
|
80
|
-
const getOriginalBindingIfStillCloseOfLinearElementEdge = (linearElement, edge,
|
|
81
|
-
const elementsMap = app.scene.getNonDeletedElementsMap();
|
|
83
|
+
const getOriginalBindingIfStillCloseOfLinearElementEdge = (linearElement, edge, elementsMap) => {
|
|
82
84
|
const coors = getLinearElementEdgeCoors(linearElement, edge, elementsMap);
|
|
83
85
|
const elementId = edge === "start"
|
|
84
86
|
? linearElement.startBinding?.elementId
|
|
85
87
|
: linearElement.endBinding?.elementId;
|
|
86
88
|
if (elementId) {
|
|
87
89
|
const element = elementsMap.get(elementId);
|
|
88
|
-
if (
|
|
90
|
+
if (isBindableElement(element) &&
|
|
91
|
+
bindingBorderTest(element, coors, elementsMap)) {
|
|
89
92
|
return element;
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
return null;
|
|
93
96
|
};
|
|
94
|
-
const getOriginalBindingsIfStillCloseToArrowEnds = (linearElement,
|
|
95
|
-
const getBindingStrategyForDraggingArrowEndpoints = (selectedElement, isBindingEnabled, draggingPoints,
|
|
97
|
+
const getOriginalBindingsIfStillCloseToArrowEnds = (linearElement, elementsMap) => ["start", "end"].map((edge) => getOriginalBindingIfStillCloseOfLinearElementEdge(linearElement, edge, elementsMap));
|
|
98
|
+
const getBindingStrategyForDraggingArrowEndpoints = (selectedElement, isBindingEnabled, draggingPoints, elementsMap, elements) => {
|
|
96
99
|
const startIdx = 0;
|
|
97
100
|
const endIdx = selectedElement.points.length - 1;
|
|
98
101
|
const startDragged = draggingPoints.findIndex((i) => i === startIdx) > -1;
|
|
99
102
|
const endDragged = draggingPoints.findIndex((i) => i === endIdx) > -1;
|
|
100
103
|
const start = startDragged
|
|
101
104
|
? isBindingEnabled
|
|
102
|
-
? getElligibleElementForBindingElement(selectedElement, "start",
|
|
105
|
+
? getElligibleElementForBindingElement(selectedElement, "start", elementsMap, elements)
|
|
103
106
|
: null // If binding is disabled and start is dragged, break all binds
|
|
104
107
|
: // We have to update the focus and gap of the binding, so let's rebind
|
|
105
|
-
getElligibleElementForBindingElement(selectedElement, "start",
|
|
108
|
+
getElligibleElementForBindingElement(selectedElement, "start", elementsMap, elements);
|
|
106
109
|
const end = endDragged
|
|
107
110
|
? isBindingEnabled
|
|
108
|
-
? getElligibleElementForBindingElement(selectedElement, "end",
|
|
111
|
+
? getElligibleElementForBindingElement(selectedElement, "end", elementsMap, elements)
|
|
109
112
|
: null // If binding is disabled and end is dragged, break all binds
|
|
110
113
|
: // We have to update the focus and gap of the binding, so let's rebind
|
|
111
|
-
getElligibleElementForBindingElement(selectedElement, "end",
|
|
114
|
+
getElligibleElementForBindingElement(selectedElement, "end", elementsMap, elements);
|
|
112
115
|
return [start, end];
|
|
113
116
|
};
|
|
114
|
-
const getBindingStrategyForDraggingArrowOrJoints = (selectedElement,
|
|
115
|
-
const [startIsClose, endIsClose] = getOriginalBindingsIfStillCloseToArrowEnds(selectedElement,
|
|
117
|
+
const getBindingStrategyForDraggingArrowOrJoints = (selectedElement, elementsMap, elements, isBindingEnabled) => {
|
|
118
|
+
const [startIsClose, endIsClose] = getOriginalBindingsIfStillCloseToArrowEnds(selectedElement, elementsMap);
|
|
116
119
|
const start = startIsClose
|
|
117
120
|
? isBindingEnabled
|
|
118
|
-
? getElligibleElementForBindingElement(selectedElement, "start",
|
|
121
|
+
? getElligibleElementForBindingElement(selectedElement, "start", elementsMap, elements)
|
|
119
122
|
: null
|
|
120
123
|
: null;
|
|
121
124
|
const end = endIsClose
|
|
122
125
|
? isBindingEnabled
|
|
123
|
-
? getElligibleElementForBindingElement(selectedElement, "end",
|
|
126
|
+
? getElligibleElementForBindingElement(selectedElement, "end", elementsMap, elements)
|
|
124
127
|
: null
|
|
125
128
|
: null;
|
|
126
129
|
return [start, end];
|
|
127
130
|
};
|
|
128
|
-
export const bindOrUnbindLinearElements = (selectedElements,
|
|
131
|
+
export const bindOrUnbindLinearElements = (selectedElements, elementsMap, elements, scene, isBindingEnabled, draggingPoints) => {
|
|
129
132
|
selectedElements.forEach((selectedElement) => {
|
|
130
133
|
const [start, end] = draggingPoints?.length
|
|
131
134
|
? // The arrow edge points are dragged (i.e. start, end)
|
|
132
|
-
getBindingStrategyForDraggingArrowEndpoints(selectedElement, isBindingEnabled, draggingPoints ?? [],
|
|
135
|
+
getBindingStrategyForDraggingArrowEndpoints(selectedElement, isBindingEnabled, draggingPoints ?? [], elementsMap, elements)
|
|
133
136
|
: // The arrow itself (the shaft) or the inner joins are dragged
|
|
134
|
-
getBindingStrategyForDraggingArrowOrJoints(selectedElement,
|
|
135
|
-
bindOrUnbindLinearElement(selectedElement, start, end,
|
|
137
|
+
getBindingStrategyForDraggingArrowOrJoints(selectedElement, elementsMap, elements, isBindingEnabled);
|
|
138
|
+
bindOrUnbindLinearElement(selectedElement, start, end, elementsMap, scene);
|
|
136
139
|
});
|
|
137
140
|
};
|
|
138
|
-
export const getSuggestedBindingsForArrows = (selectedElements,
|
|
141
|
+
export const getSuggestedBindingsForArrows = (selectedElements, elementsMap) => {
|
|
139
142
|
// HOT PATH: Bail out if selected elements list is too large
|
|
140
143
|
if (selectedElements.length > 50) {
|
|
141
144
|
return [];
|
|
142
145
|
}
|
|
143
146
|
return (selectedElements
|
|
144
147
|
.filter(isLinearElement)
|
|
145
|
-
.flatMap((element) => getOriginalBindingsIfStillCloseToArrowEnds(element,
|
|
148
|
+
.flatMap((element) => getOriginalBindingsIfStillCloseToArrowEnds(element, elementsMap))
|
|
146
149
|
.filter((element) => element !== null)
|
|
147
150
|
// Filter out bind candidates which are in the
|
|
148
151
|
// same selection / group with the arrow
|
|
@@ -151,22 +154,30 @@ export const getSuggestedBindingsForArrows = (selectedElements, app) => {
|
|
|
151
154
|
.filter((element) => selectedElements.filter((selected) => selected.id === element?.id)
|
|
152
155
|
.length === 0));
|
|
153
156
|
};
|
|
154
|
-
export const maybeBindLinearElement = (linearElement, appState, pointerCoords,
|
|
157
|
+
export const maybeBindLinearElement = (linearElement, appState, pointerCoords, elementsMap, elements) => {
|
|
155
158
|
if (appState.startBoundElement != null) {
|
|
156
|
-
bindLinearElement(linearElement, appState.startBoundElement, "start",
|
|
159
|
+
bindLinearElement(linearElement, appState.startBoundElement, "start", elementsMap);
|
|
157
160
|
}
|
|
158
|
-
const hoveredElement = getHoveredElementForBinding(pointerCoords,
|
|
159
|
-
if (hoveredElement
|
|
160
|
-
!isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(linearElement, hoveredElement, "end")) {
|
|
161
|
-
|
|
161
|
+
const hoveredElement = getHoveredElementForBinding(pointerCoords, elements, elementsMap, isElbowArrow(linearElement) && isElbowArrow(linearElement));
|
|
162
|
+
if (hoveredElement !== null) {
|
|
163
|
+
if (!isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(linearElement, hoveredElement, "end")) {
|
|
164
|
+
bindLinearElement(linearElement, hoveredElement, "end", elementsMap);
|
|
165
|
+
}
|
|
162
166
|
}
|
|
163
167
|
};
|
|
164
168
|
export const bindLinearElement = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
|
|
169
|
+
if (!isArrowElement(linearElement)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const binding = {
|
|
173
|
+
elementId: hoveredElement.id,
|
|
174
|
+
...calculateFocusAndGap(linearElement, hoveredElement, startOrEnd, elementsMap),
|
|
175
|
+
...(isElbowArrow(linearElement)
|
|
176
|
+
? calculateFixedPointForElbowArrowBinding(linearElement, hoveredElement, startOrEnd, elementsMap)
|
|
177
|
+
: { fixedPoint: null }),
|
|
178
|
+
};
|
|
165
179
|
mutateElement(linearElement, {
|
|
166
|
-
[startOrEnd === "start" ? "startBinding" : "endBinding"]:
|
|
167
|
-
elementId: hoveredElement.id,
|
|
168
|
-
...calculateFocusAndGap(linearElement, hoveredElement, startOrEnd, elementsMap),
|
|
169
|
-
},
|
|
180
|
+
[startOrEnd === "start" ? "startBinding" : "endBinding"]: binding,
|
|
170
181
|
});
|
|
171
182
|
const boundElementsMap = arrayToMap(hoveredElement.boundElements || []);
|
|
172
183
|
if (!boundElementsMap.has(linearElement.id)) {
|
|
@@ -197,9 +208,12 @@ const unbindLinearElement = (linearElement, startOrEnd) => {
|
|
|
197
208
|
mutateElement(linearElement, { [field]: null });
|
|
198
209
|
return binding.elementId;
|
|
199
210
|
};
|
|
200
|
-
export const getHoveredElementForBinding = (pointerCoords,
|
|
201
|
-
const hoveredElement = getElementAtPosition(
|
|
202
|
-
bindingBorderTest(element, pointerCoords,
|
|
211
|
+
export const getHoveredElementForBinding = (pointerCoords, elements, elementsMap, fullShape) => {
|
|
212
|
+
const hoveredElement = getElementAtPosition(elements, (element) => isBindableElement(element, false) &&
|
|
213
|
+
bindingBorderTest(element, pointerCoords, elementsMap,
|
|
214
|
+
// disable fullshape snapping for frame elements so we
|
|
215
|
+
// can bind to frame children
|
|
216
|
+
fullShape && !isFrameLikeElement(element)));
|
|
203
217
|
return hoveredElement;
|
|
204
218
|
};
|
|
205
219
|
const calculateFocusAndGap = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
|
|
@@ -218,8 +232,8 @@ const calculateFocusAndGap = (linearElement, hoveredElement, startOrEnd, element
|
|
|
218
232
|
// Because scaling involves moving the focus points as well, it is
|
|
219
233
|
// done before the `changedElement` is updated, and the `newSize` is passed
|
|
220
234
|
// in explicitly.
|
|
221
|
-
export const updateBoundElements = (changedElement, elementsMap, options) => {
|
|
222
|
-
const {
|
|
235
|
+
export const updateBoundElements = (changedElement, elementsMap, scene, options) => {
|
|
236
|
+
const { oldSize, simultaneouslyUpdated, changedElements } = options ?? {};
|
|
223
237
|
const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(simultaneouslyUpdated);
|
|
224
238
|
if (!isBindableElement(changedElement)) {
|
|
225
239
|
return;
|
|
@@ -233,20 +247,38 @@ export const updateBoundElements = (changedElement, elementsMap, options) => {
|
|
|
233
247
|
return;
|
|
234
248
|
}
|
|
235
249
|
const bindings = {
|
|
236
|
-
startBinding: maybeCalculateNewGapWhenScaling(changedElement, element.startBinding,
|
|
237
|
-
endBinding: maybeCalculateNewGapWhenScaling(changedElement, element.endBinding,
|
|
250
|
+
startBinding: maybeCalculateNewGapWhenScaling(changedElement, element.startBinding, oldSize),
|
|
251
|
+
endBinding: maybeCalculateNewGapWhenScaling(changedElement, element.endBinding, oldSize),
|
|
238
252
|
};
|
|
239
253
|
// `linearElement` is being moved/scaled already, just update the binding
|
|
240
254
|
if (simultaneouslyUpdatedElementIds.has(element.id)) {
|
|
241
255
|
mutateElement(element, bindings);
|
|
242
256
|
return;
|
|
243
257
|
}
|
|
244
|
-
bindableElementsVisitor(elementsMap, element, (bindableElement, bindingProp) => {
|
|
258
|
+
const updates = bindableElementsVisitor(elementsMap, element, (bindableElement, bindingProp) => {
|
|
245
259
|
if (bindableElement &&
|
|
246
260
|
isBindableElement(bindableElement) &&
|
|
247
|
-
(bindingProp === "startBinding" || bindingProp === "endBinding")
|
|
248
|
-
|
|
261
|
+
(bindingProp === "startBinding" || bindingProp === "endBinding") &&
|
|
262
|
+
changedElement.id === element[bindingProp]?.elementId) {
|
|
263
|
+
const point = updateBoundPoint(element, bindingProp, bindings[bindingProp], bindableElement, elementsMap);
|
|
264
|
+
if (point) {
|
|
265
|
+
return {
|
|
266
|
+
index: bindingProp === "startBinding" ? 0 : element.points.length - 1,
|
|
267
|
+
point,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
249
270
|
}
|
|
271
|
+
return null;
|
|
272
|
+
}).filter((update) => update !== null);
|
|
273
|
+
LinearElementEditor.movePoints(element, updates, scene, {
|
|
274
|
+
...(changedElement.id === element.startBinding?.elementId
|
|
275
|
+
? { startBinding: bindings.startBinding }
|
|
276
|
+
: {}),
|
|
277
|
+
...(changedElement.id === element.endBinding?.elementId
|
|
278
|
+
? { endBinding: bindings.endBinding }
|
|
279
|
+
: {}),
|
|
280
|
+
}, {
|
|
281
|
+
changedElements,
|
|
250
282
|
});
|
|
251
283
|
const boundText = getBoundTextElement(element, elementsMap);
|
|
252
284
|
if (boundText && !boundText.isDeleted) {
|
|
@@ -261,15 +293,165 @@ const doesNeedUpdate = (boundElement, changedElement) => {
|
|
|
261
293
|
const getSimultaneouslyUpdatedElementIds = (simultaneouslyUpdated) => {
|
|
262
294
|
return new Set((simultaneouslyUpdated || []).map((element) => element.id));
|
|
263
295
|
};
|
|
296
|
+
export const getHeadingForElbowArrowSnap = (point, otherPoint, bindableElement, aabb, elementsMap, origPoint) => {
|
|
297
|
+
const otherPointHeading = vectorToHeading(pointToVector(otherPoint, point));
|
|
298
|
+
if (!bindableElement || !aabb) {
|
|
299
|
+
return otherPointHeading;
|
|
300
|
+
}
|
|
301
|
+
const distance = getDistanceForBinding(origPoint, bindableElement, elementsMap);
|
|
302
|
+
if (!distance) {
|
|
303
|
+
return vectorToHeading(pointToVector(point, getCenterForElement(bindableElement)));
|
|
304
|
+
}
|
|
305
|
+
const pointHeading = headingForPointFromElement(bindableElement, aabb, point);
|
|
306
|
+
return pointHeading;
|
|
307
|
+
};
|
|
308
|
+
const getDistanceForBinding = (point, bindableElement, elementsMap) => {
|
|
309
|
+
const distance = distanceToBindableElement(bindableElement, point, elementsMap);
|
|
310
|
+
const bindDistance = maxBindingGap(bindableElement, bindableElement.width, bindableElement.height);
|
|
311
|
+
return distance > bindDistance ? null : distance;
|
|
312
|
+
};
|
|
313
|
+
export const bindPointToSnapToElementOutline = (point, otherPoint, bindableElement, elementsMap) => {
|
|
314
|
+
const aabb = bindableElement && aabbForElement(bindableElement);
|
|
315
|
+
if (bindableElement && aabb) {
|
|
316
|
+
// TODO: Dirty hack until tangents are properly calculated
|
|
317
|
+
const intersections = [
|
|
318
|
+
...intersectElementWithLine(bindableElement, [point[0], point[1] - 2 * bindableElement.height], [point[0], point[1] + 2 * bindableElement.height], FIXED_BINDING_DISTANCE, elementsMap),
|
|
319
|
+
...intersectElementWithLine(bindableElement, [point[0] - 2 * bindableElement.width, point[1]], [point[0] + 2 * bindableElement.width, point[1]], FIXED_BINDING_DISTANCE, elementsMap),
|
|
320
|
+
].map((i) => distanceToBindableElement(bindableElement, i, elementsMap) >
|
|
321
|
+
Math.min(bindableElement.width, bindableElement.height) / 2
|
|
322
|
+
? [-1 * i[0], -1 * i[1]]
|
|
323
|
+
: i);
|
|
324
|
+
const heading = headingForPointFromElement(bindableElement, aabb, point);
|
|
325
|
+
const isVertical = compareHeading(heading, HEADING_LEFT) ||
|
|
326
|
+
compareHeading(heading, HEADING_RIGHT);
|
|
327
|
+
const dist = distanceToBindableElement(bindableElement, point, elementsMap);
|
|
328
|
+
const isInner = isVertical
|
|
329
|
+
? dist < bindableElement.width * -0.1
|
|
330
|
+
: dist < bindableElement.height * -0.1;
|
|
331
|
+
intersections.sort((a, b) => distanceSq2d(a, point) - distanceSq2d(b, point));
|
|
332
|
+
return isInner
|
|
333
|
+
? headingToMidBindPoint(otherPoint, bindableElement, aabb)
|
|
334
|
+
: intersections.filter((i) => isVertical
|
|
335
|
+
? Math.abs(point[1] - i[1]) < 0.1
|
|
336
|
+
: Math.abs(point[0] - i[0]) < 0.1)[0] ?? point;
|
|
337
|
+
}
|
|
338
|
+
return point;
|
|
339
|
+
};
|
|
340
|
+
const headingToMidBindPoint = (point, bindableElement, aabb) => {
|
|
341
|
+
const center = getCenterForBounds(aabb);
|
|
342
|
+
const heading = vectorToHeading(pointToVector(point, center));
|
|
343
|
+
switch (true) {
|
|
344
|
+
case compareHeading(heading, HEADING_UP):
|
|
345
|
+
return rotatePoint([(aabb[0] + aabb[2]) / 2 + 0.1, aabb[1]], center, bindableElement.angle);
|
|
346
|
+
case compareHeading(heading, HEADING_RIGHT):
|
|
347
|
+
return rotatePoint([aabb[2], (aabb[1] + aabb[3]) / 2 + 0.1], center, bindableElement.angle);
|
|
348
|
+
case compareHeading(heading, HEADING_DOWN):
|
|
349
|
+
return rotatePoint([(aabb[0] + aabb[2]) / 2 - 0.1, aabb[3]], center, bindableElement.angle);
|
|
350
|
+
default:
|
|
351
|
+
return rotatePoint([aabb[0], (aabb[1] + aabb[3]) / 2 - 0.1], center, bindableElement.angle);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
export const avoidRectangularCorner = (element, p) => {
|
|
355
|
+
const center = getCenterForElement(element);
|
|
356
|
+
const nonRotatedPoint = rotatePoint(p, center, -element.angle);
|
|
357
|
+
if (nonRotatedPoint[0] < element.x && nonRotatedPoint[1] < element.y) {
|
|
358
|
+
// Top left
|
|
359
|
+
if (nonRotatedPoint[1] - element.y > -FIXED_BINDING_DISTANCE) {
|
|
360
|
+
return rotatePoint([element.x - FIXED_BINDING_DISTANCE, element.y], center, element.angle);
|
|
361
|
+
}
|
|
362
|
+
return rotatePoint([element.x, element.y - FIXED_BINDING_DISTANCE], center, element.angle);
|
|
363
|
+
}
|
|
364
|
+
else if (nonRotatedPoint[0] < element.x &&
|
|
365
|
+
nonRotatedPoint[1] > element.y + element.height) {
|
|
366
|
+
// Bottom left
|
|
367
|
+
if (nonRotatedPoint[0] - element.x > -FIXED_BINDING_DISTANCE) {
|
|
368
|
+
return rotatePoint([element.x, element.y + element.height + FIXED_BINDING_DISTANCE], center, element.angle);
|
|
369
|
+
}
|
|
370
|
+
return rotatePoint([element.x - FIXED_BINDING_DISTANCE, element.y + element.height], center, element.angle);
|
|
371
|
+
}
|
|
372
|
+
else if (nonRotatedPoint[0] > element.x + element.width &&
|
|
373
|
+
nonRotatedPoint[1] > element.y + element.height) {
|
|
374
|
+
// Bottom right
|
|
375
|
+
if (nonRotatedPoint[0] - element.x <
|
|
376
|
+
element.width + FIXED_BINDING_DISTANCE) {
|
|
377
|
+
return rotatePoint([
|
|
378
|
+
element.x + element.width,
|
|
379
|
+
element.y + element.height + FIXED_BINDING_DISTANCE,
|
|
380
|
+
], center, element.angle);
|
|
381
|
+
}
|
|
382
|
+
return rotatePoint([
|
|
383
|
+
element.x + element.width + FIXED_BINDING_DISTANCE,
|
|
384
|
+
element.y + element.height,
|
|
385
|
+
], center, element.angle);
|
|
386
|
+
}
|
|
387
|
+
else if (nonRotatedPoint[0] > element.x + element.width &&
|
|
388
|
+
nonRotatedPoint[1] < element.y) {
|
|
389
|
+
// Top right
|
|
390
|
+
if (nonRotatedPoint[0] - element.x <
|
|
391
|
+
element.width + FIXED_BINDING_DISTANCE) {
|
|
392
|
+
return rotatePoint([element.x + element.width, element.y - FIXED_BINDING_DISTANCE], center, element.angle);
|
|
393
|
+
}
|
|
394
|
+
return rotatePoint([element.x + element.width + FIXED_BINDING_DISTANCE, element.y], center, element.angle);
|
|
395
|
+
}
|
|
396
|
+
return p;
|
|
397
|
+
};
|
|
398
|
+
export const snapToMid = (element, p, tolerance = 0.05) => {
|
|
399
|
+
const { x, y, width, height, angle } = element;
|
|
400
|
+
const center = [x + width / 2 - 0.1, y + height / 2 - 0.1];
|
|
401
|
+
const nonRotated = rotatePoint(p, center, -angle);
|
|
402
|
+
// snap-to-center point is adaptive to element size, but we don't want to go
|
|
403
|
+
// above and below certain px distance
|
|
404
|
+
const verticalThrehsold = clamp(tolerance * height, 5, 80);
|
|
405
|
+
const horizontalThrehsold = clamp(tolerance * width, 5, 80);
|
|
406
|
+
if (nonRotated[0] <= x + width / 2 &&
|
|
407
|
+
nonRotated[1] > center[1] - verticalThrehsold &&
|
|
408
|
+
nonRotated[1] < center[1] + verticalThrehsold) {
|
|
409
|
+
// LEFT
|
|
410
|
+
return rotatePoint([x - FIXED_BINDING_DISTANCE, center[1]], center, angle);
|
|
411
|
+
}
|
|
412
|
+
else if (nonRotated[1] <= y + height / 2 &&
|
|
413
|
+
nonRotated[0] > center[0] - horizontalThrehsold &&
|
|
414
|
+
nonRotated[0] < center[0] + horizontalThrehsold) {
|
|
415
|
+
// TOP
|
|
416
|
+
return rotatePoint([center[0], y - FIXED_BINDING_DISTANCE], center, angle);
|
|
417
|
+
}
|
|
418
|
+
else if (nonRotated[0] >= x + width / 2 &&
|
|
419
|
+
nonRotated[1] > center[1] - verticalThrehsold &&
|
|
420
|
+
nonRotated[1] < center[1] + verticalThrehsold) {
|
|
421
|
+
// RIGHT
|
|
422
|
+
return rotatePoint([x + width + FIXED_BINDING_DISTANCE, center[1]], center, angle);
|
|
423
|
+
}
|
|
424
|
+
else if (nonRotated[1] >= y + height / 2 &&
|
|
425
|
+
nonRotated[0] > center[0] - horizontalThrehsold &&
|
|
426
|
+
nonRotated[0] < center[0] + horizontalThrehsold) {
|
|
427
|
+
// DOWN
|
|
428
|
+
return rotatePoint([center[0], y + height + FIXED_BINDING_DISTANCE], center, angle);
|
|
429
|
+
}
|
|
430
|
+
return p;
|
|
431
|
+
};
|
|
264
432
|
const updateBoundPoint = (linearElement, startOrEnd, binding, bindableElement, elementsMap) => {
|
|
265
433
|
if (binding == null ||
|
|
266
434
|
// We only need to update the other end if this is a 2 point line element
|
|
267
435
|
(binding.elementId !== bindableElement.id &&
|
|
268
436
|
linearElement.points.length > 2)) {
|
|
269
|
-
return;
|
|
437
|
+
return null;
|
|
270
438
|
}
|
|
271
439
|
const direction = startOrEnd === "startBinding" ? -1 : 1;
|
|
272
440
|
const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
|
|
441
|
+
if (isElbowArrow(linearElement)) {
|
|
442
|
+
const fixedPoint = binding.fixedPoint ??
|
|
443
|
+
calculateFixedPointForElbowArrowBinding(linearElement, bindableElement, startOrEnd === "startBinding" ? "start" : "end", elementsMap).fixedPoint;
|
|
444
|
+
const globalMidPoint = [
|
|
445
|
+
bindableElement.x + bindableElement.width / 2,
|
|
446
|
+
bindableElement.y + bindableElement.height / 2,
|
|
447
|
+
];
|
|
448
|
+
const global = [
|
|
449
|
+
bindableElement.x + fixedPoint[0] * bindableElement.width,
|
|
450
|
+
bindableElement.y + fixedPoint[1] * bindableElement.height,
|
|
451
|
+
];
|
|
452
|
+
const rotatedGlobal = rotatePoint(global, globalMidPoint, bindableElement.angle);
|
|
453
|
+
return LinearElementEditor.pointFromAbsoluteCoords(linearElement, rotatedGlobal, elementsMap);
|
|
454
|
+
}
|
|
273
455
|
const adjacentPointIndex = edgePointIndex - direction;
|
|
274
456
|
const adjacentPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(linearElement, adjacentPointIndex, elementsMap);
|
|
275
457
|
const focusPointAbsolute = determineFocusPoint(bindableElement, binding.focus, adjacentPoint, elementsMap);
|
|
@@ -291,25 +473,45 @@ const updateBoundPoint = (linearElement, startOrEnd, binding, bindableElement, e
|
|
|
291
473
|
newEdgePoint = intersections[0];
|
|
292
474
|
}
|
|
293
475
|
}
|
|
294
|
-
LinearElementEditor.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
476
|
+
return LinearElementEditor.pointFromAbsoluteCoords(linearElement, newEdgePoint, elementsMap);
|
|
477
|
+
};
|
|
478
|
+
export const calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
|
|
479
|
+
const bounds = [
|
|
480
|
+
hoveredElement.x,
|
|
481
|
+
hoveredElement.y,
|
|
482
|
+
hoveredElement.x + hoveredElement.width,
|
|
483
|
+
hoveredElement.y + hoveredElement.height,
|
|
484
|
+
];
|
|
485
|
+
const edgePointIndex = startOrEnd === "start" ? 0 : linearElement.points.length - 1;
|
|
486
|
+
const globalPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(linearElement, edgePointIndex, elementsMap);
|
|
487
|
+
const otherGlobalPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(linearElement, edgePointIndex, elementsMap);
|
|
488
|
+
const snappedPoint = bindPointToSnapToElementOutline(globalPoint, otherGlobalPoint, hoveredElement, elementsMap);
|
|
489
|
+
const globalMidPoint = [
|
|
490
|
+
bounds[0] + (bounds[2] - bounds[0]) / 2,
|
|
491
|
+
bounds[1] + (bounds[3] - bounds[1]) / 2,
|
|
492
|
+
];
|
|
493
|
+
const nonRotatedSnappedGlobalPoint = rotatePoint(snappedPoint, globalMidPoint, -hoveredElement.angle);
|
|
494
|
+
return {
|
|
495
|
+
fixedPoint: [
|
|
496
|
+
(nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) /
|
|
497
|
+
hoveredElement.width,
|
|
498
|
+
(nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) /
|
|
499
|
+
hoveredElement.height,
|
|
500
|
+
],
|
|
501
|
+
};
|
|
300
502
|
};
|
|
301
503
|
const maybeCalculateNewGapWhenScaling = (changedElement, currentBinding, newSize) => {
|
|
302
504
|
if (currentBinding == null || newSize == null) {
|
|
303
505
|
return currentBinding;
|
|
304
506
|
}
|
|
305
|
-
const { gap, focus, elementId } = currentBinding;
|
|
306
507
|
const { width: newWidth, height: newHeight } = newSize;
|
|
307
508
|
const { width, height } = changedElement;
|
|
308
|
-
const newGap = Math.max(1, Math.min(maxBindingGap(changedElement, newWidth, newHeight), gap *
|
|
309
|
-
|
|
509
|
+
const newGap = Math.max(1, Math.min(maxBindingGap(changedElement, newWidth, newHeight), currentBinding.gap *
|
|
510
|
+
(newWidth < newHeight ? newWidth / width : newHeight / height)));
|
|
511
|
+
return { ...currentBinding, gap: newGap };
|
|
310
512
|
};
|
|
311
|
-
const getElligibleElementForBindingElement = (linearElement, startOrEnd,
|
|
312
|
-
return getHoveredElementForBinding(getLinearElementEdgeCoors(linearElement, startOrEnd,
|
|
513
|
+
const getElligibleElementForBindingElement = (linearElement, startOrEnd, elementsMap, elements) => {
|
|
514
|
+
return getHoveredElementForBinding(getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap), elements, elementsMap);
|
|
313
515
|
};
|
|
314
516
|
const getLinearElementEdgeCoors = (linearElement, startOrEnd, elementsMap) => {
|
|
315
517
|
const index = startOrEnd === "start" ? 0 : -1;
|
|
@@ -329,6 +531,7 @@ duplicatesServeAsOld) => {
|
|
|
329
531
|
const allBoundElementIds = new Set();
|
|
330
532
|
const allBindableElementIds = new Set();
|
|
331
533
|
const shouldReverseRoles = duplicatesServeAsOld === "duplicatesServeAsOld";
|
|
534
|
+
const duplicateIdToOldId = new Map([...oldIdToDuplicatedId].map(([key, value]) => [value, key]));
|
|
332
535
|
oldElements.forEach((oldElement) => {
|
|
333
536
|
const { boundElements } = oldElement;
|
|
334
537
|
if (boundElements != null && boundElements.length > 0) {
|
|
@@ -369,7 +572,8 @@ duplicatesServeAsOld) => {
|
|
|
369
572
|
sceneElements
|
|
370
573
|
.filter(({ id }) => allBindableElementIds.has(id))
|
|
371
574
|
.forEach((bindableElement) => {
|
|
372
|
-
const
|
|
575
|
+
const oldElementId = duplicateIdToOldId.get(bindableElement.id);
|
|
576
|
+
const { boundElements } = sceneElements.find(({ id }) => id === oldElementId);
|
|
373
577
|
if (boundElements != null && boundElements.length > 0) {
|
|
374
578
|
mutateElement(bindableElement, {
|
|
375
579
|
boundElements: boundElements.map((boundElement) => oldIdToDuplicatedId.has(boundElement.id)
|
|
@@ -386,11 +590,9 @@ const newBindingAfterDuplication = (binding, oldIdToDuplicatedId) => {
|
|
|
386
590
|
if (binding == null) {
|
|
387
591
|
return null;
|
|
388
592
|
}
|
|
389
|
-
const { elementId, focus, gap } = binding;
|
|
390
593
|
return {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
elementId: oldIdToDuplicatedId.get(elementId) ?? elementId,
|
|
594
|
+
...binding,
|
|
595
|
+
elementId: oldIdToDuplicatedId.get(binding.elementId) ?? binding.elementId,
|
|
394
596
|
};
|
|
395
597
|
};
|
|
396
598
|
export const fixBindingsAfterDeletion = (sceneElements, deletedElements) => {
|
|
@@ -408,10 +610,11 @@ const newBoundElements = (boundElements, idsToRemove, elementsToAdd = []) => {
|
|
|
408
610
|
nextBoundElements.push(...elementsToAdd.map((x) => ({ id: x.id, type: x.type })));
|
|
409
611
|
return nextBoundElements;
|
|
410
612
|
};
|
|
411
|
-
const bindingBorderTest = (element, { x, y },
|
|
613
|
+
export const bindingBorderTest = (element, { x, y }, elementsMap, fullShape) => {
|
|
412
614
|
const threshold = maxBindingGap(element, element.width, element.height);
|
|
413
|
-
const shape =
|
|
414
|
-
return isPointOnShape([x, y], shape, threshold)
|
|
615
|
+
const shape = getElementShape(element, elementsMap);
|
|
616
|
+
return (isPointOnShape([x, y], shape, threshold) ||
|
|
617
|
+
(fullShape === true && pointInsideBounds([x, y], aabbForElement(element))));
|
|
415
618
|
};
|
|
416
619
|
export const maxBindingGap = (element, elementWidth, elementHeight) => {
|
|
417
620
|
// Aligns diamonds with rectangles
|
|
@@ -420,7 +623,7 @@ export const maxBindingGap = (element, elementWidth, elementHeight) => {
|
|
|
420
623
|
// We make the bindable boundary bigger for bigger elements
|
|
421
624
|
return Math.max(16, Math.min(0.25 * smallerDimension, 32));
|
|
422
625
|
};
|
|
423
|
-
const distanceToBindableElement = (element, point, elementsMap) => {
|
|
626
|
+
export const distanceToBindableElement = (element, point, elementsMap) => {
|
|
424
627
|
switch (element.type) {
|
|
425
628
|
case "rectangle":
|
|
426
629
|
case "image":
|
|
@@ -803,24 +1006,26 @@ const boundElementsVisitor = (elements, element, visit) => {
|
|
|
803
1006
|
* Tries to visit each bindable element (does not have to be found).
|
|
804
1007
|
*/
|
|
805
1008
|
const bindableElementsVisitor = (elements, element, visit) => {
|
|
1009
|
+
const result = [];
|
|
806
1010
|
if (element.frameId) {
|
|
807
1011
|
const id = element.frameId;
|
|
808
|
-
visit(elements.get(id), "frameId", id);
|
|
1012
|
+
result.push(visit(elements.get(id), "frameId", id));
|
|
809
1013
|
}
|
|
810
1014
|
if (isBoundToContainer(element)) {
|
|
811
1015
|
const id = element.containerId;
|
|
812
|
-
visit(elements.get(id), "containerId", id);
|
|
1016
|
+
result.push(visit(elements.get(id), "containerId", id));
|
|
813
1017
|
}
|
|
814
1018
|
if (isArrowElement(element)) {
|
|
815
1019
|
if (element.startBinding) {
|
|
816
1020
|
const id = element.startBinding.elementId;
|
|
817
|
-
visit(elements.get(id), "startBinding", id);
|
|
1021
|
+
result.push(visit(elements.get(id), "startBinding", id));
|
|
818
1022
|
}
|
|
819
1023
|
if (element.endBinding) {
|
|
820
1024
|
const id = element.endBinding.elementId;
|
|
821
|
-
visit(elements.get(id), "endBinding", id);
|
|
1025
|
+
result.push(visit(elements.get(id), "endBinding", id));
|
|
822
1026
|
}
|
|
823
1027
|
}
|
|
1028
|
+
return result;
|
|
824
1029
|
};
|
|
825
1030
|
/**
|
|
826
1031
|
* Bound element containing bindings to `frameId`, `containerId`, `startBinding` or `endBinding`.
|
|
@@ -969,3 +1174,30 @@ export class BindableElement {
|
|
|
969
1174
|
});
|
|
970
1175
|
};
|
|
971
1176
|
}
|
|
1177
|
+
export const getGlobalFixedPointForBindableElement = (fixedPointRatio, element) => {
|
|
1178
|
+
const [fixedX, fixedY] = fixedPointRatio;
|
|
1179
|
+
return rotatePoint([element.x + element.width * fixedX, element.y + element.height * fixedY], getCenterForElement(element), element.angle);
|
|
1180
|
+
};
|
|
1181
|
+
const getGlobalFixedPoints = (arrow, elementsMap) => {
|
|
1182
|
+
const startElement = arrow.startBinding &&
|
|
1183
|
+
elementsMap.get(arrow.startBinding.elementId);
|
|
1184
|
+
const endElement = arrow.endBinding &&
|
|
1185
|
+
elementsMap.get(arrow.endBinding.elementId);
|
|
1186
|
+
const startPoint = startElement && arrow.startBinding
|
|
1187
|
+
? getGlobalFixedPointForBindableElement(arrow.startBinding.fixedPoint, startElement)
|
|
1188
|
+
: [arrow.x + arrow.points[0][0], arrow.y + arrow.points[0][1]];
|
|
1189
|
+
const endPoint = endElement && arrow.endBinding
|
|
1190
|
+
? getGlobalFixedPointForBindableElement(arrow.endBinding.fixedPoint, endElement)
|
|
1191
|
+
: [
|
|
1192
|
+
arrow.x + arrow.points[arrow.points.length - 1][0],
|
|
1193
|
+
arrow.y + arrow.points[arrow.points.length - 1][1],
|
|
1194
|
+
];
|
|
1195
|
+
return [startPoint, endPoint];
|
|
1196
|
+
};
|
|
1197
|
+
export const getArrowLocalFixedPoints = (arrow, elementsMap) => {
|
|
1198
|
+
const [startPoint, endPoint] = getGlobalFixedPoints(arrow, elementsMap);
|
|
1199
|
+
return [
|
|
1200
|
+
LinearElementEditor.pointFromAbsoluteCoords(arrow, startPoint, elementsMap),
|
|
1201
|
+
LinearElementEditor.pointFromAbsoluteCoords(arrow, endPoint, elementsMap),
|
|
1202
|
+
];
|
|
1203
|
+
};
|
|
@@ -13,4 +13,4 @@ export type HitTestArgs = {
|
|
|
13
13
|
export declare const hitElementItself: ({ x, y, element, shape, threshold, frameNameBound, }: HitTestArgs) => boolean;
|
|
14
14
|
export declare const hitElementBoundingBox: (x: number, y: number, element: ExcalidrawElement, elementsMap: ElementsMap, tolerance?: number) => boolean;
|
|
15
15
|
export declare const hitElementBoundingBoxOnly: (hitArgs: HitTestArgs, elementsMap: ElementsMap) => boolean;
|
|
16
|
-
export declare const hitElementBoundText: (x: number, y: number, textShape: GeometricShape | null) => boolean
|
|
16
|
+
export declare const hitElementBoundText: (x: number, y: number, textShape: GeometricShape | null) => boolean;
|
|
@@ -4,6 +4,7 @@ import { getPolygonShape } from "../../utils/geometry/shape";
|
|
|
4
4
|
import { isPointInShape, isPointOnShape } from "../../utils/collision";
|
|
5
5
|
import { isTransparent } from "../utils";
|
|
6
6
|
import { hasBoundTextElement, isIframeLikeElement, isImageElement, isTextElement, } from "./typeChecks";
|
|
7
|
+
import { getBoundTextShape } from "../shapes";
|
|
7
8
|
export const shouldTestInside = (element) => {
|
|
8
9
|
if (element.type === "arrow") {
|
|
9
10
|
return false;
|
|
@@ -46,8 +47,10 @@ export const hitElementBoundingBox = (x, y, element, elementsMap, tolerance = 0)
|
|
|
46
47
|
};
|
|
47
48
|
export const hitElementBoundingBoxOnly = (hitArgs, elementsMap) => {
|
|
48
49
|
return (!hitElementItself(hitArgs) &&
|
|
50
|
+
// bound text is considered part of the element (even if it's outside the bounding box)
|
|
51
|
+
!hitElementBoundText(hitArgs.x, hitArgs.y, getBoundTextShape(hitArgs.element, elementsMap)) &&
|
|
49
52
|
hitElementBoundingBox(hitArgs.x, hitArgs.y, hitArgs.element, elementsMap));
|
|
50
53
|
};
|
|
51
54
|
export const hitElementBoundText = (x, y, textShape) => {
|
|
52
|
-
return textShape && isPointInShape([x, y], textShape);
|
|
55
|
+
return !!textShape && isPointInShape([x, y], textShape);
|
|
53
56
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { NonDeletedExcalidrawElement } from "./types";
|
|
2
2
|
import type { AppState, NormalizedZoomValue, PointerDownState } from "../types";
|
|
3
3
|
import type Scene from "../scene/Scene";
|
|
4
|
-
export declare const dragSelectedElements: (pointerDownState: PointerDownState,
|
|
4
|
+
export declare const dragSelectedElements: (pointerDownState: PointerDownState, _selectedElements: NonDeletedExcalidrawElement[], offset: {
|
|
5
5
|
x: number;
|
|
6
6
|
y: number;
|
|
7
|
-
},
|
|
7
|
+
}, scene: Scene, snapOffset: {
|
|
8
8
|
x: number;
|
|
9
9
|
y: number;
|
|
10
10
|
}, gridSize: AppState["gridSize"]) => void;
|