@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.
Files changed (252) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/browser/dev/excalidraw-assets-dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  7. package/dist/browser/dev/excalidraw-assets-dev/{chunk-EM6LVGFW.js → chunk-IT7T3AIK.js} +23 -5
  8. package/dist/browser/dev/excalidraw-assets-dev/chunk-IT7T3AIK.js.map +7 -0
  9. package/dist/browser/dev/excalidraw-assets-dev/{chunk-B4UMSLQQ.js → chunk-RNHSD5AR.js} +7451 -2098
  10. package/dist/browser/dev/excalidraw-assets-dev/chunk-RNHSD5AR.js.map +7 -0
  11. package/dist/browser/dev/excalidraw-assets-dev/{dist-6QVAH5JA.js → dist-DNSPZDOZ.js} +31 -19
  12. package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
  13. package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js → en-XV7OZCPP.js} +6 -2
  14. package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js → image-77HZYGLG.js} +2 -2
  15. package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css → image-WDHYGKKP.css} +1 -1
  16. package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css.map → image-WDHYGKKP.css.map} +2 -2
  17. package/dist/browser/dev/index.css +449 -114
  18. package/dist/browser/dev/index.css.map +3 -3
  19. package/dist/browser/dev/index.js +4143 -5956
  20. package/dist/browser/dev/index.js.map +4 -4
  21. package/dist/browser/prod/excalidraw-assets/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  22. package/dist/browser/prod/excalidraw-assets/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  23. package/dist/browser/prod/excalidraw-assets/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  24. package/dist/browser/prod/excalidraw-assets/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  25. package/dist/browser/prod/excalidraw-assets/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  26. package/dist/browser/prod/excalidraw-assets/chunk-OYEADJSR.js +63 -0
  27. package/dist/browser/prod/excalidraw-assets/{chunk-7DXALCB2.js → chunk-PDYFZJMS.js} +3 -3
  28. package/dist/browser/prod/excalidraw-assets/dist-NLUQPPQQ.js +7 -0
  29. package/dist/browser/prod/excalidraw-assets/en-YVAVVILW.js +1 -0
  30. package/dist/browser/prod/excalidraw-assets/image-X3GFZHNN.js +1 -0
  31. package/dist/browser/prod/index.css +1 -1
  32. package/dist/browser/prod/index.js +40 -50
  33. package/dist/dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  34. package/dist/dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  35. package/dist/dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  36. package/dist/dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  37. package/dist/dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  38. package/dist/dev/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
  39. package/dist/dev/index.css +449 -114
  40. package/dist/dev/index.css.map +3 -3
  41. package/dist/dev/index.js +21626 -18122
  42. package/dist/dev/index.js.map +4 -4
  43. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +9 -3
  44. package/dist/excalidraw/actions/actionBoundText.d.ts +6 -2
  45. package/dist/excalidraw/actions/actionCanvas.d.ts +36 -12
  46. package/dist/excalidraw/actions/actionClipboard.d.ts +22 -7
  47. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +12 -5
  48. package/dist/excalidraw/actions/actionDeleteSelected.js +24 -5
  49. package/dist/excalidraw/actions/actionDuplicateSelection.js +1 -2
  50. package/dist/excalidraw/actions/actionElementLock.d.ts +6 -2
  51. package/dist/excalidraw/actions/actionExport.d.ts +27 -9
  52. package/dist/excalidraw/actions/actionFinalize.d.ts +6 -2
  53. package/dist/excalidraw/actions/actionFinalize.js +2 -2
  54. package/dist/excalidraw/actions/actionFlip.js +2 -2
  55. package/dist/excalidraw/actions/actionFrame.d.ts +12 -4
  56. package/dist/excalidraw/actions/actionGroup.d.ts +6 -2
  57. package/dist/excalidraw/actions/actionHistory.js +4 -4
  58. package/dist/excalidraw/actions/actionLinearEditor.d.ts +3 -1
  59. package/dist/excalidraw/actions/actionLinearEditor.js +3 -2
  60. package/dist/excalidraw/actions/actionLink.d.ts +3 -1
  61. package/dist/excalidraw/actions/actionMenu.d.ts +9 -3
  62. package/dist/excalidraw/actions/actionNavigate.d.ts +6 -2
  63. package/dist/excalidraw/actions/actionProperties.d.ts +411 -56
  64. package/dist/excalidraw/actions/actionProperties.js +383 -58
  65. package/dist/excalidraw/actions/actionSelectAll.d.ts +3 -1
  66. package/dist/excalidraw/actions/actionStyles.d.ts +3 -1
  67. package/dist/excalidraw/actions/actionStyles.js +3 -2
  68. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +3 -1
  69. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +3 -1
  70. package/dist/excalidraw/actions/actionToggleStats.d.ts +3 -1
  71. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +3 -1
  72. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +3 -1
  73. package/dist/excalidraw/actions/types.d.ts +1 -1
  74. package/dist/excalidraw/analytics.js +9 -7
  75. package/dist/excalidraw/appState.d.ts +1 -0
  76. package/dist/excalidraw/appState.js +9 -1
  77. package/dist/excalidraw/binaryheap.d.ts +12 -0
  78. package/dist/excalidraw/binaryheap.js +93 -0
  79. package/dist/excalidraw/change.d.ts +2 -1
  80. package/dist/excalidraw/change.js +6 -4
  81. package/dist/excalidraw/charts.js +0 -10
  82. package/dist/excalidraw/components/Actions.js +7 -5
  83. package/dist/excalidraw/components/App.d.ts +5 -9
  84. package/dist/excalidraw/components/App.js +218 -161
  85. package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
  86. package/dist/excalidraw/components/ButtonIcon.js +8 -0
  87. package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
  88. package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
  89. package/dist/excalidraw/components/ButtonSeparator.js +7 -0
  90. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +47 -79
  91. package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
  92. package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
  93. package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
  94. package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
  95. package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
  96. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
  97. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
  98. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
  99. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
  100. package/dist/excalidraw/components/HelpDialog.js +1 -1
  101. package/dist/excalidraw/components/HintViewer.js +6 -3
  102. package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
  103. package/dist/excalidraw/components/PropertiesPopover.js +31 -0
  104. package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
  105. package/dist/excalidraw/components/QuickSearch.js +8 -0
  106. package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
  107. package/dist/excalidraw/components/ScrollableList.js +8 -0
  108. package/dist/excalidraw/components/Stats/Angle.d.ts +7 -3
  109. package/dist/excalidraw/components/Stats/Angle.js +39 -31
  110. package/dist/excalidraw/components/Stats/Dimension.d.ts +6 -3
  111. package/dist/excalidraw/components/Stats/Dimension.js +51 -49
  112. package/dist/excalidraw/components/Stats/DragInput.d.ts +15 -6
  113. package/dist/excalidraw/components/Stats/DragInput.js +59 -26
  114. package/dist/excalidraw/components/Stats/FontSize.d.ts +8 -4
  115. package/dist/excalidraw/components/Stats/FontSize.js +39 -36
  116. package/dist/excalidraw/components/Stats/MultiAngle.d.ts +5 -3
  117. package/dist/excalidraw/components/Stats/MultiAngle.js +43 -34
  118. package/dist/excalidraw/components/Stats/MultiDimension.d.ts +5 -3
  119. package/dist/excalidraw/components/Stats/MultiDimension.js +101 -99
  120. package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +6 -3
  121. package/dist/excalidraw/components/Stats/MultiFontSize.js +47 -32
  122. package/dist/excalidraw/components/Stats/MultiPosition.d.ts +3 -1
  123. package/dist/excalidraw/components/Stats/MultiPosition.js +52 -48
  124. package/dist/excalidraw/components/Stats/Position.d.ts +5 -1
  125. package/dist/excalidraw/components/Stats/Position.js +31 -29
  126. package/dist/excalidraw/components/Stats/index.js +5 -17
  127. package/dist/excalidraw/components/Stats/utils.d.ts +14 -3
  128. package/dist/excalidraw/components/Stats/utils.js +48 -9
  129. package/dist/excalidraw/components/TTDDialog/common.d.ts +2 -2
  130. package/dist/excalidraw/components/TTDDialog/common.js +3 -7
  131. package/dist/excalidraw/components/UserList.js +22 -22
  132. package/dist/excalidraw/components/canvases/StaticCanvas.js +1 -0
  133. package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
  134. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
  135. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
  136. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
  137. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
  138. package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
  139. package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
  140. package/dist/excalidraw/components/icons.d.ts +4 -0
  141. package/dist/excalidraw/components/icons.js +7 -0
  142. package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
  143. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
  144. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
  145. package/dist/excalidraw/constants.d.ts +17 -2
  146. package/dist/excalidraw/constants.js +21 -4
  147. package/dist/excalidraw/data/reconcile.js +18 -1
  148. package/dist/excalidraw/data/restore.js +55 -9
  149. package/dist/excalidraw/data/transform.js +8 -5
  150. package/dist/excalidraw/element/binding.d.ts +28 -9
  151. package/dist/excalidraw/element/binding.js +303 -71
  152. package/dist/excalidraw/element/collision.d.ts +1 -1
  153. package/dist/excalidraw/element/collision.js +4 -1
  154. package/dist/excalidraw/element/dragElements.d.ts +2 -2
  155. package/dist/excalidraw/element/dragElements.js +13 -3
  156. package/dist/excalidraw/element/embeddable.d.ts +3 -1
  157. package/dist/excalidraw/element/heading.d.ts +11 -0
  158. package/dist/excalidraw/element/heading.js +81 -0
  159. package/dist/excalidraw/element/index.d.ts +1 -1
  160. package/dist/excalidraw/element/index.js +1 -1
  161. package/dist/excalidraw/element/linearElementEditor.d.ts +21 -13
  162. package/dist/excalidraw/element/linearElementEditor.js +133 -56
  163. package/dist/excalidraw/element/newElement.d.ts +8 -3
  164. package/dist/excalidraw/element/newElement.js +15 -2
  165. package/dist/excalidraw/element/resizeElements.d.ts +4 -3
  166. package/dist/excalidraw/element/resizeElements.js +47 -23
  167. package/dist/excalidraw/element/routing.d.ts +13 -0
  168. package/dist/excalidraw/element/routing.js +641 -0
  169. package/dist/excalidraw/element/textElement.d.ts +3 -26
  170. package/dist/excalidraw/element/textElement.js +54 -110
  171. package/dist/excalidraw/element/textWysiwyg.js +39 -47
  172. package/dist/excalidraw/element/transformHandles.js +7 -2
  173. package/dist/excalidraw/element/typeChecks.d.ts +5 -2
  174. package/dist/excalidraw/element/typeChecks.js +17 -0
  175. package/dist/excalidraw/element/types.d.ts +12 -1
  176. package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +21 -0
  177. package/dist/excalidraw/fonts/ExcalidrawFont.js +112 -0
  178. package/dist/excalidraw/fonts/index.d.ts +58 -0
  179. package/dist/excalidraw/fonts/index.js +240 -0
  180. package/dist/excalidraw/fonts/metadata.d.ts +36 -0
  181. package/dist/excalidraw/fonts/metadata.js +91 -0
  182. package/dist/excalidraw/fractionalIndex.d.ts +11 -4
  183. package/dist/excalidraw/fractionalIndex.js +38 -6
  184. package/dist/excalidraw/frame.d.ts +1 -1
  185. package/dist/excalidraw/frame.js +3 -3
  186. package/dist/excalidraw/history.d.ts +4 -3
  187. package/dist/excalidraw/history.js +8 -8
  188. package/dist/excalidraw/index.d.ts +1 -1
  189. package/dist/excalidraw/index.js +3 -3
  190. package/dist/excalidraw/locales/en.json +18 -4
  191. package/dist/excalidraw/math.d.ts +43 -0
  192. package/dist/excalidraw/math.js +110 -0
  193. package/dist/excalidraw/mermaid.js +4 -3
  194. package/dist/excalidraw/renderer/interactiveScene.js +33 -17
  195. package/dist/excalidraw/renderer/renderElement.d.ts +2 -0
  196. package/dist/excalidraw/renderer/renderElement.js +74 -54
  197. package/dist/excalidraw/renderer/staticSvgScene.js +2 -1
  198. package/dist/excalidraw/scene/Scene.js +9 -3
  199. package/dist/excalidraw/scene/Shape.js +56 -5
  200. package/dist/excalidraw/scene/comparisons.d.ts +1 -0
  201. package/dist/excalidraw/scene/comparisons.js +1 -1
  202. package/dist/excalidraw/scene/export.d.ts +2 -1
  203. package/dist/excalidraw/scene/export.js +33 -35
  204. package/dist/excalidraw/scene/types.d.ts +1 -4
  205. package/dist/excalidraw/shapes.d.ts +8 -0
  206. package/dist/excalidraw/shapes.js +57 -0
  207. package/dist/excalidraw/types.d.ts +8 -3
  208. package/dist/excalidraw/utils.d.ts +11 -1
  209. package/dist/excalidraw/utils.js +22 -0
  210. package/dist/prod/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  211. package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  212. package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  213. package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  214. package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  215. package/dist/prod/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
  216. package/dist/prod/index.css +1 -1
  217. package/dist/prod/index.js +49 -53
  218. package/dist/utils/export.d.ts +2 -1
  219. package/dist/utils/export.js +2 -1
  220. package/dist/utils/geometry/geometry.d.ts +2 -1
  221. package/dist/utils/geometry/geometry.js +5 -1
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.js +1 -0
  224. package/history.ts +9 -2
  225. package/package.json +2 -2
  226. package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  227. package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
  228. package/dist/browser/dev/excalidraw-assets-dev/chunk-B4UMSLQQ.js.map +0 -7
  229. package/dist/browser/dev/excalidraw-assets-dev/chunk-EM6LVGFW.js.map +0 -7
  230. package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +0 -7
  231. package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  232. package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
  233. package/dist/browser/prod/excalidraw-assets/chunk-EGOLGOLD.js +0 -55
  234. package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +0 -7
  235. package/dist/browser/prod/excalidraw-assets/en-6E7MYLWO.js +0 -1
  236. package/dist/browser/prod/excalidraw-assets/image-SI7BKULC.js +0 -1
  237. package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  238. package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
  239. package/dist/excalidraw/scene/Fonts.d.ts +0 -19
  240. package/dist/excalidraw/scene/Fonts.js +0 -66
  241. package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  242. package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
  243. /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  244. /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  245. /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  246. /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
  247. /package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js.map → en-XV7OZCPP.js.map} +0 -0
  248. /package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js.map → image-77HZYGLG.js.map} +0 -0
  249. /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  250. /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  251. /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  252. /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(Scene.getScene(linearElement), onlyUnbound).forEach((element) => {
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, app) => {
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 (bindingBorderTest(element, coors, app)) {
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, app) => ["start", "end"].map((edge) => getOriginalBindingIfStillCloseOfLinearElementEdge(linearElement, edge, app));
95
- const getBindingStrategyForDraggingArrowEndpoints = (selectedElement, isBindingEnabled, draggingPoints, app) => {
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", app)
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", app);
108
+ getElligibleElementForBindingElement(selectedElement, "start", elementsMap, elements);
106
109
  const end = endDragged
107
110
  ? isBindingEnabled
108
- ? getElligibleElementForBindingElement(selectedElement, "end", app)
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", app);
114
+ getElligibleElementForBindingElement(selectedElement, "end", elementsMap, elements);
112
115
  return [start, end];
113
116
  };
114
- const getBindingStrategyForDraggingArrowOrJoints = (selectedElement, app, isBindingEnabled) => {
115
- const [startIsClose, endIsClose] = getOriginalBindingsIfStillCloseToArrowEnds(selectedElement, app);
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", app)
121
+ ? getElligibleElementForBindingElement(selectedElement, "start", elementsMap, elements)
119
122
  : null
120
123
  : null;
121
124
  const end = endIsClose
122
125
  ? isBindingEnabled
123
- ? getElligibleElementForBindingElement(selectedElement, "end", app)
126
+ ? getElligibleElementForBindingElement(selectedElement, "end", elementsMap, elements)
124
127
  : null
125
128
  : null;
126
129
  return [start, end];
127
130
  };
128
- export const bindOrUnbindLinearElements = (selectedElements, app, isBindingEnabled, draggingPoints) => {
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 ?? [], app)
135
+ getBindingStrategyForDraggingArrowEndpoints(selectedElement, isBindingEnabled, draggingPoints ?? [], elementsMap, elements)
133
136
  : // The arrow itself (the shaft) or the inner joins are dragged
134
- getBindingStrategyForDraggingArrowOrJoints(selectedElement, app, isBindingEnabled);
135
- bindOrUnbindLinearElement(selectedElement, start, end, app.scene.getNonDeletedElementsMap());
137
+ getBindingStrategyForDraggingArrowOrJoints(selectedElement, elementsMap, elements, isBindingEnabled);
138
+ bindOrUnbindLinearElement(selectedElement, start, end, elementsMap, scene);
136
139
  });
137
140
  };
138
- export const getSuggestedBindingsForArrows = (selectedElements, app) => {
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, app))
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, app) => {
157
+ export const maybeBindLinearElement = (linearElement, appState, pointerCoords, elementsMap, elements) => {
155
158
  if (appState.startBoundElement != null) {
156
- bindLinearElement(linearElement, appState.startBoundElement, "start", app.scene.getNonDeletedElementsMap());
159
+ bindLinearElement(linearElement, appState.startBoundElement, "start", elementsMap);
157
160
  }
158
- const hoveredElement = getHoveredElementForBinding(pointerCoords, app);
159
- if (hoveredElement != null &&
160
- !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(linearElement, hoveredElement, "end")) {
161
- bindLinearElement(linearElement, hoveredElement, "end", app.scene.getNonDeletedElementsMap());
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, app) => {
201
- const hoveredElement = getElementAtPosition(app.scene.getNonDeletedElements(), (element) => isBindableElement(element, false) &&
202
- bindingBorderTest(element, pointerCoords, app));
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 { newSize, simultaneouslyUpdated } = options ?? {};
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, newSize),
237
- endBinding: maybeCalculateNewGapWhenScaling(changedElement, element.endBinding, newSize),
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
- updateBoundPoint(element, bindingProp, bindings[bindingProp], bindableElement, elementsMap);
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.movePoints(linearElement, [
295
- {
296
- index: edgePointIndex,
297
- point: LinearElementEditor.pointFromAbsoluteCoords(linearElement, newEdgePoint, elementsMap),
298
- },
299
- ], { [startOrEnd]: binding });
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 * (newWidth < newHeight ? newWidth / width : newHeight / height)));
309
- return { elementId, gap: newGap, focus };
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, app) => {
312
- return getHoveredElementForBinding(getLinearElementEdgeCoors(linearElement, startOrEnd, app.scene.getNonDeletedElementsMap()), app);
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 { boundElements } = bindableElement;
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
- focus,
392
- gap,
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 }, app) => {
613
+ export const bindingBorderTest = (element, { x, y }, elementsMap, fullShape) => {
412
614
  const threshold = maxBindingGap(element, element.width, element.height);
413
- const shape = app.getElementShape(element);
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 | null;
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, selectedElements: NonDeletedExcalidrawElement[], offset: {
4
+ export declare const dragSelectedElements: (pointerDownState: PointerDownState, _selectedElements: NonDeletedExcalidrawElement[], offset: {
5
5
  x: number;
6
6
  y: number;
7
- }, appState: AppState, scene: Scene, snapOffset: {
7
+ }, scene: Scene, snapOffset: {
8
8
  x: number;
9
9
  y: number;
10
10
  }, gridSize: AppState["gridSize"]) => void;