@excalidraw/excalidraw 0.17.1-7500-ac247a0 → 0.17.1-a38e82f

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 (251) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js +20279 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js.map +7 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/{chunk-2W5GQUR4.js → chunk-IM4WTX2M.js} +12 -6
  5. package/dist/browser/dev/excalidraw-assets-dev/chunk-IM4WTX2M.js.map +7 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js → en-IOBA4CS2.js} +4 -2
  7. package/dist/browser/dev/excalidraw-assets-dev/image-LK4UNFRZ.css +6 -0
  8. package/dist/browser/dev/excalidraw-assets-dev/image-LK4UNFRZ.css.map +7 -0
  9. package/dist/browser/dev/excalidraw-assets-dev/{image-5TVMINCA.js → image-VKDAL6BQ.js} +2 -4
  10. package/dist/browser/dev/excalidraw-assets-dev/roundRect-T5BX56ZF.js +161 -0
  11. package/dist/browser/dev/excalidraw-assets-dev/roundRect-T5BX56ZF.js.map +7 -0
  12. package/dist/browser/dev/index.css +189 -129
  13. package/dist/browser/dev/index.css.map +3 -3
  14. package/dist/browser/dev/index.js +34708 -37
  15. package/dist/browser/dev/index.js.map +4 -4
  16. package/dist/browser/prod/excalidraw-assets/chunk-LIG3S5TN.js +11 -0
  17. package/dist/browser/prod/excalidraw-assets/chunk-N2C5DK3B.js +55 -0
  18. package/dist/browser/prod/excalidraw-assets/en-WFZVQ7I6.js +1 -0
  19. package/dist/browser/prod/excalidraw-assets/image-4AT7LYMR.js +1 -0
  20. package/dist/browser/prod/excalidraw-assets/image-X66R2EM5.css +1 -0
  21. package/dist/browser/prod/excalidraw-assets/roundRect-2ACQK4DA.js +1 -0
  22. package/dist/browser/prod/index.css +1 -1
  23. package/dist/browser/prod/index.js +203 -1
  24. package/dist/{prod/en-RLIAOBCI.json → dev/en-TDNWCAOT.json} +9 -5
  25. package/dist/dev/index.css +189 -129
  26. package/dist/dev/index.css.map +3 -3
  27. package/dist/dev/index.js +39078 -40080
  28. package/dist/dev/index.js.map +4 -4
  29. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +15 -15
  30. package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
  31. package/dist/excalidraw/actions/actionAlign.js +2 -1
  32. package/dist/excalidraw/actions/actionBoundText.d.ts +10 -10
  33. package/dist/excalidraw/actions/actionBoundText.js +8 -8
  34. package/dist/excalidraw/actions/actionCanvas.d.ts +58 -58
  35. package/dist/excalidraw/actions/actionClipboard.d.ts +34 -34
  36. package/dist/excalidraw/actions/actionClipboard.js +9 -2
  37. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +15 -15
  38. package/dist/excalidraw/actions/actionDeleteSelected.js +3 -2
  39. package/dist/excalidraw/actions/actionDistribute.d.ts +2 -2
  40. package/dist/excalidraw/actions/actionDistribute.js +1 -1
  41. package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +1 -1
  42. package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -3
  43. package/dist/excalidraw/actions/actionElementLock.d.ts +10 -10
  44. package/dist/excalidraw/actions/actionExport.d.ts +43 -43
  45. package/dist/excalidraw/actions/actionExport.js +4 -4
  46. package/dist/excalidraw/actions/actionFinalize.d.ts +9 -9
  47. package/dist/excalidraw/actions/actionFinalize.js +7 -6
  48. package/dist/excalidraw/actions/actionFlip.d.ts +2 -2
  49. package/dist/excalidraw/actions/actionFlip.js +11 -11
  50. package/dist/excalidraw/actions/actionFrame.d.ts +16 -16
  51. package/dist/excalidraw/actions/actionFrame.js +1 -1
  52. package/dist/excalidraw/actions/actionGroup.d.ts +10 -10
  53. package/dist/excalidraw/actions/actionGroup.js +3 -2
  54. package/dist/excalidraw/actions/actionLinearEditor.d.ts +5 -5
  55. package/dist/excalidraw/actions/actionLinearEditor.js +1 -1
  56. package/dist/excalidraw/{element/Hyperlink.d.ts → actions/actionLink.d.ts} +29 -51
  57. package/dist/excalidraw/actions/actionLink.js +40 -0
  58. package/dist/excalidraw/actions/actionMenu.d.ts +13 -13
  59. package/dist/excalidraw/actions/actionNavigate.d.ts +10 -10
  60. package/dist/excalidraw/actions/actionNavigate.js +1 -1
  61. package/dist/excalidraw/actions/actionProperties.d.ts +77 -77
  62. package/dist/excalidraw/actions/actionProperties.js +32 -27
  63. package/dist/excalidraw/actions/actionSelectAll.d.ts +5 -5
  64. package/dist/excalidraw/actions/actionSelectAll.js +1 -1
  65. package/dist/excalidraw/actions/actionStyles.d.ts +7 -7
  66. package/dist/excalidraw/actions/actionStyles.js +4 -4
  67. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +5 -5
  68. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +5 -5
  69. package/dist/excalidraw/actions/actionToggleStats.d.ts +5 -5
  70. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +5 -5
  71. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +5 -5
  72. package/dist/excalidraw/actions/index.d.ts +1 -1
  73. package/dist/excalidraw/actions/index.js +1 -1
  74. package/dist/excalidraw/actions/manager.js +2 -1
  75. package/dist/excalidraw/align.d.ts +2 -2
  76. package/dist/excalidraw/align.js +2 -2
  77. package/dist/excalidraw/animated-trail.d.ts +33 -0
  78. package/dist/excalidraw/animated-trail.js +96 -0
  79. package/dist/excalidraw/animation-frame-handler.d.ts +16 -0
  80. package/dist/excalidraw/animation-frame-handler.js +55 -0
  81. package/dist/excalidraw/appState.d.ts +1 -1
  82. package/dist/excalidraw/appState.js +1 -3
  83. package/dist/excalidraw/clipboard.js +5 -5
  84. package/dist/excalidraw/components/Actions.d.ts +3 -3
  85. package/dist/excalidraw/components/Actions.js +18 -7
  86. package/dist/excalidraw/components/App.d.ts +23 -16
  87. package/dist/excalidraw/components/App.js +387 -272
  88. package/dist/excalidraw/components/Button.d.ts +1 -1
  89. package/dist/excalidraw/components/FilledButton.d.ts +2 -2
  90. package/dist/excalidraw/components/FilledButton.js +27 -3
  91. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  92. package/dist/excalidraw/components/ImageExportDialog.d.ts +2 -1
  93. package/dist/excalidraw/components/ImageExportDialog.js +17 -13
  94. package/dist/excalidraw/components/JSONExportDialog.js +1 -1
  95. package/dist/excalidraw/components/{LaserTool/LaserPointerButton.d.ts → LaserPointerButton.d.ts} +1 -1
  96. package/dist/excalidraw/components/{LaserTool/LaserPointerButton.js → LaserPointerButton.js} +2 -2
  97. package/dist/excalidraw/components/LayerUI.js +3 -3
  98. package/dist/excalidraw/components/MobileMenu.js +1 -1
  99. package/dist/excalidraw/components/ProjectName.d.ts +0 -1
  100. package/dist/excalidraw/components/ProjectName.js +1 -1
  101. package/dist/excalidraw/components/PublishLibrary.js +1 -1
  102. package/dist/excalidraw/components/SVGLayer.d.ts +8 -0
  103. package/dist/excalidraw/components/SVGLayer.js +20 -0
  104. package/dist/excalidraw/components/ShareableLinkDialog.js +10 -10
  105. package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
  106. package/dist/excalidraw/components/Stack.d.ts +2 -2
  107. package/dist/excalidraw/components/TTDDialog/common.js +10 -1
  108. package/dist/excalidraw/components/TextField.d.ts +5 -2
  109. package/dist/excalidraw/components/TextField.js +6 -3
  110. package/dist/excalidraw/components/Toast.d.ts +3 -2
  111. package/dist/excalidraw/components/Toast.js +2 -2
  112. package/dist/excalidraw/components/ToolButton.js +2 -1
  113. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +2 -2
  114. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +6 -5
  115. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +4 -3
  116. package/dist/excalidraw/components/canvases/StaticCanvas.js +7 -5
  117. package/dist/excalidraw/components/dropdownMenu/DropdownMenuContent.js +22 -2
  118. package/dist/excalidraw/components/hyperlink/Hyperlink.d.ts +19 -0
  119. package/dist/excalidraw/{element → components/hyperlink}/Hyperlink.js +40 -115
  120. package/dist/excalidraw/components/hyperlink/helpers.d.ts +7 -0
  121. package/dist/excalidraw/components/hyperlink/helpers.js +49 -0
  122. package/dist/excalidraw/components/icons.d.ts +2 -1
  123. package/dist/excalidraw/components/icons.js +2 -1
  124. package/dist/excalidraw/components/live-collaboration/LiveCollaborationTrigger.js +3 -2
  125. package/dist/excalidraw/components/main-menu/DefaultItems.js +5 -2
  126. package/dist/excalidraw/constants.d.ts +6 -0
  127. package/dist/excalidraw/constants.js +6 -0
  128. package/dist/excalidraw/data/blob.js +13 -14
  129. package/dist/excalidraw/data/filesystem.d.ts +1 -1
  130. package/dist/excalidraw/data/index.d.ts +2 -1
  131. package/dist/excalidraw/data/index.js +20 -16
  132. package/dist/excalidraw/data/json.d.ts +1 -1
  133. package/dist/excalidraw/data/json.js +5 -3
  134. package/dist/excalidraw/data/resave.d.ts +1 -1
  135. package/dist/excalidraw/data/resave.js +2 -2
  136. package/dist/excalidraw/data/restore.js +8 -13
  137. package/dist/excalidraw/data/transform.js +13 -9
  138. package/dist/excalidraw/distribute.d.ts +2 -2
  139. package/dist/excalidraw/distribute.js +2 -2
  140. package/dist/excalidraw/element/ElementCanvasButtons.d.ts +3 -2
  141. package/dist/excalidraw/element/ElementCanvasButtons.js +4 -4
  142. package/dist/excalidraw/element/binding.d.ts +9 -9
  143. package/dist/excalidraw/element/binding.js +61 -59
  144. package/dist/excalidraw/element/bounds.d.ts +5 -5
  145. package/dist/excalidraw/element/bounds.js +29 -32
  146. package/dist/excalidraw/element/collision.d.ts +11 -11
  147. package/dist/excalidraw/element/collision.js +49 -46
  148. package/dist/excalidraw/element/containerCache.d.ts +11 -0
  149. package/dist/excalidraw/element/containerCache.js +14 -0
  150. package/dist/excalidraw/element/dragElements.js +10 -19
  151. package/dist/excalidraw/element/embeddable.d.ts +12 -13
  152. package/dist/excalidraw/element/embeddable.js +17 -27
  153. package/dist/excalidraw/element/image.js +1 -2
  154. package/dist/excalidraw/element/index.d.ts +0 -1
  155. package/dist/excalidraw/element/index.js +0 -1
  156. package/dist/excalidraw/element/linearElementEditor.d.ts +36 -36
  157. package/dist/excalidraw/element/linearElementEditor.js +79 -80
  158. package/dist/excalidraw/element/newElement.d.ts +4 -6
  159. package/dist/excalidraw/element/newElement.js +11 -16
  160. package/dist/excalidraw/element/resizeElements.d.ts +6 -6
  161. package/dist/excalidraw/element/resizeElements.js +40 -46
  162. package/dist/excalidraw/element/resizeTest.d.ts +3 -3
  163. package/dist/excalidraw/element/resizeTest.js +4 -4
  164. package/dist/excalidraw/element/sizeHelpers.d.ts +2 -2
  165. package/dist/excalidraw/element/sizeHelpers.js +2 -2
  166. package/dist/excalidraw/element/textElement.d.ts +18 -20
  167. package/dist/excalidraw/element/textElement.js +80 -111
  168. package/dist/excalidraw/element/textWysiwyg.d.ts +1 -6
  169. package/dist/excalidraw/element/textWysiwyg.js +15 -37
  170. package/dist/excalidraw/element/transformHandles.d.ts +4 -4
  171. package/dist/excalidraw/element/transformHandles.js +6 -6
  172. package/dist/excalidraw/element/typeChecks.js +4 -1
  173. package/dist/excalidraw/element/types.d.ts +24 -11
  174. package/dist/excalidraw/frame.d.ts +26 -20
  175. package/dist/excalidraw/frame.js +157 -84
  176. package/dist/excalidraw/groups.d.ts +3 -3
  177. package/dist/excalidraw/groups.js +11 -3
  178. package/dist/excalidraw/history.d.ts +1 -1
  179. package/dist/excalidraw/hooks/useLibraryItemSvg.js +1 -1
  180. package/dist/excalidraw/index.d.ts +8 -9
  181. package/dist/excalidraw/index.js +15 -11
  182. package/dist/excalidraw/laser-trails.d.ts +19 -0
  183. package/dist/excalidraw/laser-trails.js +95 -0
  184. package/dist/excalidraw/locales/en.json +9 -5
  185. package/dist/excalidraw/reactUtils.d.ts +14 -0
  186. package/dist/excalidraw/reactUtils.js +45 -0
  187. package/dist/excalidraw/renderer/helpers.d.ts +13 -0
  188. package/dist/excalidraw/renderer/helpers.js +39 -0
  189. package/dist/excalidraw/renderer/interactiveScene.d.ts +20 -0
  190. package/dist/excalidraw/renderer/{renderScene.js → interactiveScene.js} +199 -474
  191. package/dist/excalidraw/renderer/renderElement.d.ts +6 -6
  192. package/dist/excalidraw/renderer/renderElement.js +54 -366
  193. package/dist/excalidraw/renderer/staticScene.d.ts +11 -0
  194. package/dist/excalidraw/renderer/staticScene.js +205 -0
  195. package/dist/excalidraw/renderer/staticSvgScene.d.ts +5 -0
  196. package/dist/excalidraw/renderer/staticSvgScene.js +385 -0
  197. package/dist/excalidraw/scene/Fonts.js +2 -1
  198. package/dist/excalidraw/scene/Renderer.d.ts +1 -1
  199. package/dist/excalidraw/scene/Renderer.js +32 -20
  200. package/dist/excalidraw/scene/Scene.d.ts +10 -9
  201. package/dist/excalidraw/scene/Scene.js +45 -21
  202. package/dist/excalidraw/scene/Shape.d.ts +3 -1
  203. package/dist/excalidraw/scene/Shape.js +7 -5
  204. package/dist/excalidraw/scene/ShapeCache.d.ts +2 -1
  205. package/dist/excalidraw/scene/ShapeCache.js +1 -0
  206. package/dist/excalidraw/scene/comparisons.js +2 -1
  207. package/dist/excalidraw/scene/export.d.ts +3 -0
  208. package/dist/excalidraw/scene/export.js +20 -40
  209. package/dist/excalidraw/scene/index.d.ts +0 -1
  210. package/dist/excalidraw/scene/index.js +0 -1
  211. package/dist/excalidraw/scene/scrollbars.d.ts +1 -1
  212. package/dist/excalidraw/scene/scrollbars.js +1 -1
  213. package/dist/excalidraw/scene/selection.d.ts +5 -5
  214. package/dist/excalidraw/scene/selection.js +16 -14
  215. package/dist/excalidraw/scene/types.d.ts +11 -5
  216. package/dist/excalidraw/snapping.d.ts +7 -7
  217. package/dist/excalidraw/snapping.js +21 -20
  218. package/dist/excalidraw/types.d.ts +10 -11
  219. package/dist/excalidraw/utility-types.d.ts +5 -0
  220. package/dist/excalidraw/utils.d.ts +18 -15
  221. package/dist/excalidraw/utils.js +37 -45
  222. package/dist/{dev/en-RLIAOBCI.json → prod/en-TDNWCAOT.json} +9 -5
  223. package/dist/prod/index.css +1 -1
  224. package/dist/prod/index.js +42 -42
  225. package/dist/utils/bbox.d.ts +2 -2
  226. package/dist/utils/export.d.ts +3 -3
  227. package/dist/utils/export.js +3 -13
  228. package/dist/utils/index.d.ts +2 -2
  229. package/dist/utils/index.js +2 -2
  230. package/dist/utils/withinBounds.d.ts +1 -1
  231. package/dist/utils/withinBounds.js +5 -2
  232. package/package.json +4 -4
  233. package/dist/browser/dev/excalidraw-assets-dev/chunk-2W5GQUR4.js.map +0 -7
  234. package/dist/browser/dev/excalidraw-assets-dev/chunk-KGZXLFLR.js +0 -53497
  235. package/dist/browser/dev/excalidraw-assets-dev/chunk-KGZXLFLR.js.map +0 -7
  236. package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css +0 -5797
  237. package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css.map +0 -7
  238. package/dist/browser/prod/excalidraw-assets/chunk-4YN2HN3S.js +0 -257
  239. package/dist/browser/prod/excalidraw-assets/chunk-OWLL6VOG.js +0 -11
  240. package/dist/browser/prod/excalidraw-assets/en-ERQOR3OC.js +0 -1
  241. package/dist/browser/prod/excalidraw-assets/image-LTLHTTSE.js +0 -1
  242. package/dist/browser/prod/excalidraw-assets/image-QBL334OA.css +0 -1
  243. package/dist/excalidraw/components/LaserTool/LaserPathManager.d.ts +0 -28
  244. package/dist/excalidraw/components/LaserTool/LaserPathManager.js +0 -225
  245. package/dist/excalidraw/components/LaserTool/LaserTool.d.ts +0 -8
  246. package/dist/excalidraw/components/LaserTool/LaserTool.js +0 -15
  247. package/dist/excalidraw/renderer/renderScene.d.ts +0 -25
  248. package/dist/excalidraw/vite.config.d.mts +0 -2
  249. package/dist/excalidraw/vite.config.mjs +0 -13
  250. /package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js.map → en-IOBA4CS2.js.map} +0 -0
  251. /package/dist/browser/dev/excalidraw-assets-dev/{image-5TVMINCA.js.map → image-VKDAL6BQ.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { Point } from "../types";
2
2
  import { FONT_FAMILY, ROUNDNESS, TEXT_ALIGN, THEME, VERTICAL_ALIGN } from "../constants";
3
- import { MarkNonNullable, ValueOf } from "../utility-types";
3
+ import { MakeBrand, MarkNonNullable, ValueOf } from "../utility-types";
4
4
  import { MagicCacheData } from "../data/magic";
5
5
  export type ChartType = "bar" | "line";
6
6
  export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag";
@@ -76,14 +76,6 @@ export type ExcalidrawEllipseElement = _ExcalidrawElementBase & {
76
76
  };
77
77
  export type ExcalidrawEmbeddableElement = _ExcalidrawElementBase & Readonly<{
78
78
  type: "embeddable";
79
- /**
80
- * indicates whether the embeddable src (url) has been validated for rendering.
81
- * null value indicates that the validation is pending. We reset the
82
- * value on each restore (or url change) so that we can guarantee
83
- * the validation came from a trusted source (the editor). Also because we
84
- * may not have access to host-app supplied url validator during restore.
85
- */
86
- validated: boolean | null;
87
79
  }>;
88
80
  export type ExcalidrawIframeElement = _ExcalidrawElementBase & Readonly<{
89
81
  type: "iframe";
@@ -97,7 +89,7 @@ export type IframeData = ({
97
89
  w: number;
98
90
  h: number;
99
91
  };
100
- warning?: string;
92
+ error?: Error;
101
93
  } & ({
102
94
  type: "video" | "generic";
103
95
  link: string;
@@ -142,7 +134,6 @@ export type ExcalidrawTextElement = _ExcalidrawElementBase & Readonly<{
142
134
  fontSize: number;
143
135
  fontFamily: FontFamilyValues;
144
136
  text: string;
145
- baseline: number;
146
137
  textAlign: TextAlign;
147
138
  verticalAlign: VerticalAlign;
148
139
  containerId: ExcalidrawGenericElement["id"] | null;
@@ -189,4 +180,26 @@ export type FileId = string & {
189
180
  _brand: "FileId";
190
181
  };
191
182
  export type ExcalidrawElementType = ExcalidrawElement["type"];
183
+ /**
184
+ * Map of excalidraw elements.
185
+ * Unspecified whether deleted or non-deleted.
186
+ * Can be a subset of Scene elements.
187
+ */
188
+ export type ElementsMap = Map<ExcalidrawElement["id"], ExcalidrawElement>;
189
+ /**
190
+ * Map of non-deleted elements.
191
+ * Can be a subset of Scene elements.
192
+ */
193
+ export type NonDeletedElementsMap = Map<ExcalidrawElement["id"], NonDeletedExcalidrawElement> & MakeBrand<"NonDeletedElementsMap">;
194
+ /**
195
+ * Map of all excalidraw Scene elements, including deleted.
196
+ * Not a subset. Use this type when you need access to current Scene elements.
197
+ */
198
+ export type SceneElementsMap = Map<ExcalidrawElement["id"], ExcalidrawElement> & MakeBrand<"SceneElementsMap">;
199
+ /**
200
+ * Map of all non-deleted Scene elements.
201
+ * Not a subset. Use this type when you need access to current Scene elements.
202
+ */
203
+ export type NonDeletedSceneElementsMap = Map<ExcalidrawElement["id"], NonDeletedExcalidrawElement> & MakeBrand<"NonDeletedSceneElementsMap">;
204
+ export type ElementsMapOrArray = readonly ExcalidrawElement[] | Readonly<ElementsMap>;
192
205
  export {};
@@ -1,24 +1,25 @@
1
- import { ExcalidrawElement, ExcalidrawFrameLikeElement, NonDeleted, NonDeletedExcalidrawElement } from "./element/types";
1
+ import { ElementsMap, ElementsMapOrArray, ExcalidrawElement, ExcalidrawFrameLikeElement, NonDeleted, NonDeletedExcalidrawElement } from "./element/types";
2
2
  import { AppClassProperties, AppState, StaticCanvasAppState } from "./types";
3
- import { ExcalidrawElementsIncludingDeleted } from "./scene/Scene";
3
+ import type { ExcalidrawElementsIncludingDeleted } from "./scene/Scene";
4
+ import { ReadonlySetLike } from "./utility-types";
4
5
  export declare const bindElementsToFramesAfterDuplication: (nextElements: ExcalidrawElement[], oldElements: readonly ExcalidrawElement[], oldIdToDuplicatedId: Map<ExcalidrawElement["id"], ExcalidrawElement["id"]>) => void;
5
- export declare function isElementIntersectingFrame(element: ExcalidrawElement, frame: ExcalidrawFrameLikeElement): boolean;
6
- export declare const getElementsCompletelyInFrame: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => ExcalidrawElement[];
7
- export declare const isElementContainingFrame: (elements: readonly ExcalidrawElement[], element: ExcalidrawElement, frame: ExcalidrawFrameLikeElement) => boolean;
6
+ export declare function isElementIntersectingFrame(element: ExcalidrawElement, frame: ExcalidrawFrameLikeElement, elementsMap: ElementsMap): boolean;
7
+ export declare const getElementsCompletelyInFrame: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement, elementsMap: ElementsMap) => ExcalidrawElement[];
8
+ export declare const isElementContainingFrame: (elements: readonly ExcalidrawElement[], element: ExcalidrawElement, frame: ExcalidrawFrameLikeElement, elementsMap: ElementsMap) => boolean;
8
9
  export declare const getElementsIntersectingFrame: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => ExcalidrawElement[];
9
- export declare const elementsAreInFrameBounds: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => boolean;
10
- export declare const elementOverlapsWithFrame: (element: ExcalidrawElement, frame: ExcalidrawFrameLikeElement) => boolean;
10
+ export declare const elementsAreInFrameBounds: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement, elementsMap: ElementsMap) => boolean;
11
+ export declare const elementOverlapsWithFrame: (element: ExcalidrawElement, frame: ExcalidrawFrameLikeElement, elementsMap: ElementsMap) => boolean;
11
12
  export declare const isCursorInFrame: (cursorCoords: {
12
13
  x: number;
13
14
  y: number;
14
- }, frame: NonDeleted<ExcalidrawFrameLikeElement>) => boolean;
15
+ }, frame: NonDeleted<ExcalidrawFrameLikeElement>, elementsMap: ElementsMap) => boolean;
15
16
  export declare const groupsAreAtLeastIntersectingTheFrame: (elements: readonly NonDeletedExcalidrawElement[], groupIds: readonly string[], frame: ExcalidrawFrameLikeElement) => boolean;
16
17
  export declare const groupsAreCompletelyOutOfFrame: (elements: readonly NonDeletedExcalidrawElement[], groupIds: readonly string[], frame: ExcalidrawFrameLikeElement) => boolean;
17
18
  /**
18
19
  * Returns a map of frameId to frame elements. Includes empty frames.
19
20
  */
20
21
  export declare const groupByFrameLikes: (elements: readonly ExcalidrawElement[]) => Map<string, ExcalidrawElement[]>;
21
- export declare const getFrameChildren: (allElements: ExcalidrawElementsIncludingDeleted, frameId: string) => ExcalidrawElement[];
22
+ export declare const getFrameChildren: (allElements: ElementsMapOrArray, frameId: string) => ExcalidrawElement[];
22
23
  export declare const getFrameLikeElements: (allElements: ExcalidrawElementsIncludingDeleted) => ExcalidrawFrameLikeElement[];
23
24
  /**
24
25
  * Returns ExcalidrawFrameElements and non-frame-children elements.
@@ -29,29 +30,34 @@ export declare const getFrameLikeElements: (allElements: ExcalidrawElementsInclu
29
30
  * Considers non-frame bound elements (container or arrow labels) as root.
30
31
  */
31
32
  export declare const getRootElements: (allElements: ExcalidrawElementsIncludingDeleted) => ExcalidrawElement[];
32
- export declare const getElementsInResizingFrame: (allElements: ExcalidrawElementsIncludingDeleted, frame: ExcalidrawFrameLikeElement, appState: AppState) => ExcalidrawElement[];
33
- export declare const getElementsInNewFrame: (allElements: ExcalidrawElementsIncludingDeleted, frame: ExcalidrawFrameLikeElement) => ExcalidrawElement[];
34
- export declare const getContainingFrame: (element: ExcalidrawElement, elementsMap?: Map<string, ExcalidrawElement>) => ExcalidrawFrameLikeElement | null;
33
+ export declare const getElementsInResizingFrame: (allElements: ExcalidrawElementsIncludingDeleted, frame: ExcalidrawFrameLikeElement, appState: AppState, elementsMap: ElementsMap) => ExcalidrawElement[];
34
+ export declare const getElementsInNewFrame: (elements: ExcalidrawElementsIncludingDeleted, frame: ExcalidrawFrameLikeElement, elementsMap: ElementsMap) => ExcalidrawElement[];
35
+ export declare const getContainingFrame: (element: ExcalidrawElement, elementsMap: ElementsMap) => ExcalidrawFrameLikeElement | null;
36
+ /** */
37
+ export declare const filterElementsEligibleAsFrameChildren: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => ExcalidrawElement[];
35
38
  /**
36
39
  * Retains (or repairs for target frame) the ordering invriant where children
37
40
  * elements come right before the parent frame:
38
41
  * [el, el, child, child, frame, el]
42
+ *
43
+ * @returns mutated allElements (same data structure)
39
44
  */
40
- export declare const addElementsToFrame: (allElements: ExcalidrawElementsIncludingDeleted, elementsToAdd: NonDeletedExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => ExcalidrawElement[];
41
- export declare const removeElementsFromFrame: (allElements: ExcalidrawElementsIncludingDeleted, elementsToRemove: NonDeletedExcalidrawElement[], appState: AppState) => ExcalidrawElement[];
42
- export declare const removeAllElementsFromFrame: (allElements: ExcalidrawElementsIncludingDeleted, frame: ExcalidrawFrameLikeElement, appState: AppState) => ExcalidrawElement[];
43
- export declare const replaceAllElementsInFrame: (allElements: ExcalidrawElementsIncludingDeleted, nextElementsInFrame: ExcalidrawElement[], frame: ExcalidrawFrameLikeElement, appState: AppState) => ExcalidrawElement[];
45
+ export declare const addElementsToFrame: <T extends ElementsMapOrArray>(allElements: T, elementsToAdd: NonDeletedExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => T;
46
+ export declare const removeElementsFromFrame: (elementsToRemove: ReadonlySetLike<NonDeletedExcalidrawElement>, elementsMap: ElementsMap) => void;
47
+ export declare const removeAllElementsFromFrame: <T extends ExcalidrawElement>(allElements: readonly T[], frame: ExcalidrawFrameLikeElement) => readonly T[];
48
+ export declare const replaceAllElementsInFrame: <T extends ExcalidrawElement>(allElements: readonly T[], nextElementsInFrame: ExcalidrawElement[], frame: ExcalidrawFrameLikeElement, app: AppClassProperties) => T[];
44
49
  /** does not mutate elements, but returns new ones */
45
- export declare const updateFrameMembershipOfSelectedElements: (allElements: ExcalidrawElementsIncludingDeleted, appState: AppState, app: AppClassProperties) => ExcalidrawElementsIncludingDeleted;
50
+ export declare const updateFrameMembershipOfSelectedElements: <T extends ElementsMapOrArray>(allElements: T, appState: AppState, app: AppClassProperties) => T;
46
51
  /**
47
52
  * filters out elements that are inside groups that contain a frame element
48
53
  * anywhere in the group tree
49
54
  */
50
- export declare const omitGroupsContainingFrameLikes: (allElements: ExcalidrawElementsIncludingDeleted, selectedElements?: readonly ExcalidrawElement[]) => ExcalidrawElement[];
55
+ export declare const omitGroupsContainingFrameLikes: (allElements: ElementsMapOrArray, selectedElements?: readonly ExcalidrawElement[]) => ExcalidrawElement[];
51
56
  /**
52
57
  * depending on the appState, return target frame, which is the frame the given element
53
58
  * is going to be added to or remove from
54
59
  */
55
- export declare const getTargetFrame: (element: ExcalidrawElement, appState: StaticCanvasAppState) => import("./element/types").ExcalidrawFrameElement | import("./element/types").ExcalidrawMagicFrameElement | null;
56
- export declare const isElementInFrame: (element: ExcalidrawElement, allElements: ExcalidrawElementsIncludingDeleted, appState: StaticCanvasAppState) => boolean;
60
+ export declare const getTargetFrame: (element: ExcalidrawElement, elementsMap: ElementsMap, appState: StaticCanvasAppState) => import("./element/types").ExcalidrawFrameElement | import("./element/types").ExcalidrawMagicFrameElement | null;
61
+ export declare const isElementInFrame: (element: ExcalidrawElement, allElementsMap: ElementsMap, appState: StaticCanvasAppState) => boolean;
57
62
  export declare const getFrameLikeTitle: (element: ExcalidrawFrameLikeElement, frameIdx: number) => string;
63
+ export declare const getElementsOverlappingFrame: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => NonDeletedExcalidrawElement[];
@@ -5,9 +5,8 @@ import { arrayToMap } from "./utils";
5
5
  import { mutateElement } from "./element/mutateElement";
6
6
  import { getElementsWithinSelection, getSelectedElements } from "./scene";
7
7
  import { getElementsInGroup, selectGroupsFromGivenElements } from "./groups";
8
- import Scene from "./scene/Scene";
9
8
  import { getElementLineSegments } from "./element/bounds";
10
- import { doLineSegmentsIntersect } from "../utils/index";
9
+ import { doLineSegmentsIntersect, elementsOverlappingBBox } from "../utils/";
11
10
  import { isFrameElement, isFrameLikeElement } from "./element/typeChecks";
12
11
  // --------------------------- Frame State ------------------------------------
13
12
  export const bindElementsToFramesAfterDuplication = (nextElements, oldElements, oldIdToDuplicatedId) => {
@@ -28,50 +27,55 @@ export const bindElementsToFramesAfterDuplication = (nextElements, oldElements,
28
27
  }
29
28
  }
30
29
  };
31
- export function isElementIntersectingFrame(element, frame) {
32
- const frameLineSegments = getElementLineSegments(frame);
33
- const elementLineSegments = getElementLineSegments(element);
30
+ export function isElementIntersectingFrame(element, frame, elementsMap) {
31
+ const frameLineSegments = getElementLineSegments(frame, elementsMap);
32
+ const elementLineSegments = getElementLineSegments(element, elementsMap);
34
33
  const intersecting = frameLineSegments.some((frameLineSegment) => elementLineSegments.some((elementLineSegment) => doLineSegmentsIntersect(frameLineSegment, elementLineSegment)));
35
34
  return intersecting;
36
35
  }
37
- export const getElementsCompletelyInFrame = (elements, frame) => omitGroupsContainingFrameLikes(getElementsWithinSelection(elements, frame, false)).filter((element) => (!isFrameLikeElement(element) && !element.frameId) ||
36
+ export const getElementsCompletelyInFrame = (elements, frame, elementsMap) => omitGroupsContainingFrameLikes(getElementsWithinSelection(elements, frame, elementsMap, false)).filter((element) => (!isFrameLikeElement(element) && !element.frameId) ||
38
37
  element.frameId === frame.id);
39
- export const isElementContainingFrame = (elements, element, frame) => {
40
- return getElementsWithinSelection(elements, element).some((e) => e.id === frame.id);
38
+ export const isElementContainingFrame = (elements, element, frame, elementsMap) => {
39
+ return getElementsWithinSelection(elements, element, elementsMap).some((e) => e.id === frame.id);
41
40
  };
42
- export const getElementsIntersectingFrame = (elements, frame) => elements.filter((element) => isElementIntersectingFrame(element, frame));
43
- export const elementsAreInFrameBounds = (elements, frame) => {
44
- const [selectionX1, selectionY1, selectionX2, selectionY2] = getElementAbsoluteCoords(frame);
41
+ export const getElementsIntersectingFrame = (elements, frame) => {
42
+ const elementsMap = arrayToMap(elements);
43
+ return elements.filter((element) => isElementIntersectingFrame(element, frame, elementsMap));
44
+ };
45
+ export const elementsAreInFrameBounds = (elements, frame, elementsMap) => {
46
+ const [frameX1, frameY1, frameX2, frameY2] = getElementAbsoluteCoords(frame, elementsMap);
45
47
  const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(elements);
46
- return (selectionX1 <= elementX1 &&
47
- selectionY1 <= elementY1 &&
48
- selectionX2 >= elementX2 &&
49
- selectionY2 >= elementY2);
48
+ return (frameX1 <= elementX1 &&
49
+ frameY1 <= elementY1 &&
50
+ frameX2 >= elementX2 &&
51
+ frameY2 >= elementY2);
50
52
  };
51
- export const elementOverlapsWithFrame = (element, frame) => {
52
- return (elementsAreInFrameBounds([element], frame) ||
53
- isElementIntersectingFrame(element, frame) ||
54
- isElementContainingFrame([frame], element, frame));
53
+ export const elementOverlapsWithFrame = (element, frame, elementsMap) => {
54
+ return (elementsAreInFrameBounds([element], frame, elementsMap) ||
55
+ isElementIntersectingFrame(element, frame, elementsMap) ||
56
+ isElementContainingFrame([frame], element, frame, elementsMap));
55
57
  };
56
- export const isCursorInFrame = (cursorCoords, frame) => {
57
- const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords(frame);
58
+ export const isCursorInFrame = (cursorCoords, frame, elementsMap) => {
59
+ const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords(frame, elementsMap);
58
60
  return isPointWithinBounds([fx1, fy1], [cursorCoords.x, cursorCoords.y], [fx2, fy2]);
59
61
  };
60
62
  export const groupsAreAtLeastIntersectingTheFrame = (elements, groupIds, frame) => {
63
+ const elementsMap = arrayToMap(elements);
61
64
  const elementsInGroup = groupIds.flatMap((groupId) => getElementsInGroup(elements, groupId));
62
65
  if (elementsInGroup.length === 0) {
63
66
  return true;
64
67
  }
65
- return !!elementsInGroup.find((element) => elementsAreInFrameBounds([element], frame) ||
66
- isElementIntersectingFrame(element, frame));
68
+ return !!elementsInGroup.find((element) => elementsAreInFrameBounds([element], frame, elementsMap) ||
69
+ isElementIntersectingFrame(element, frame, elementsMap));
67
70
  };
68
71
  export const groupsAreCompletelyOutOfFrame = (elements, groupIds, frame) => {
72
+ const elementsMap = arrayToMap(elements);
69
73
  const elementsInGroup = groupIds.flatMap((groupId) => getElementsInGroup(elements, groupId));
70
74
  if (elementsInGroup.length === 0) {
71
75
  return true;
72
76
  }
73
- return (elementsInGroup.find((element) => elementsAreInFrameBounds([element], frame) ||
74
- isElementIntersectingFrame(element, frame)) === undefined);
77
+ return (elementsInGroup.find((element) => elementsAreInFrameBounds([element], frame, elementsMap) ||
78
+ isElementIntersectingFrame(element, frame, elementsMap)) === undefined);
75
79
  };
76
80
  // --------------------------- Frame Utils ------------------------------------
77
81
  /**
@@ -87,7 +91,15 @@ export const groupByFrameLikes = (elements) => {
87
91
  }
88
92
  return frameElementsMap;
89
93
  };
90
- export const getFrameChildren = (allElements, frameId) => allElements.filter((element) => element.frameId === frameId);
94
+ export const getFrameChildren = (allElements, frameId) => {
95
+ const frameChildren = [];
96
+ for (const element of allElements.values()) {
97
+ if (element.frameId === frameId) {
98
+ frameChildren.push(element);
99
+ }
100
+ }
101
+ return frameChildren;
102
+ };
91
103
  export const getFrameLikeElements = (allElements) => {
92
104
  return allElements.filter((element) => isFrameLikeElement(element));
93
105
  };
@@ -105,12 +117,12 @@ export const getRootElements = (allElements) => {
105
117
  !element.frameId ||
106
118
  !frameElements.has(element.frameId));
107
119
  };
108
- export const getElementsInResizingFrame = (allElements, frame, appState) => {
120
+ export const getElementsInResizingFrame = (allElements, frame, appState, elementsMap) => {
109
121
  const prevElementsInFrame = getFrameChildren(allElements, frame.id);
110
122
  const nextElementsInFrame = new Set(prevElementsInFrame);
111
123
  const elementsCompletelyInFrame = new Set([
112
- ...getElementsCompletelyInFrame(allElements, frame),
113
- ...prevElementsInFrame.filter((element) => isElementContainingFrame(allElements, element, frame)),
124
+ ...getElementsCompletelyInFrame(allElements, frame, elementsMap),
125
+ ...prevElementsInFrame.filter((element) => isElementContainingFrame(allElements, element, frame, elementsMap)),
114
126
  ]);
115
127
  const elementsNotCompletelyInFrame = prevElementsInFrame.filter((element) => !elementsCompletelyInFrame.has(element));
116
128
  // for elements that are completely in the frame
@@ -118,7 +130,7 @@ export const getElementsInResizingFrame = (allElements, frame, appState) => {
118
130
  // considered to belong to the frame
119
131
  const groupsToKeep = new Set(Array.from(elementsCompletelyInFrame).flatMap((element) => element.groupIds));
120
132
  for (const element of elementsNotCompletelyInFrame) {
121
- if (!isElementIntersectingFrame(element, frame)) {
133
+ if (!isElementIntersectingFrame(element, frame, elementsMap)) {
122
134
  if (element.groupIds.length === 0) {
123
135
  nextElementsInFrame.delete(element);
124
136
  }
@@ -154,7 +166,7 @@ export const getElementsInResizingFrame = (allElements, frame, appState) => {
154
166
  for (const [id, isSelected] of Object.entries(groupIds)) {
155
167
  if (isSelected) {
156
168
  const elementsInGroup = getElementsInGroup(allElements, id);
157
- if (elementsAreInFrameBounds(elementsInGroup, frame)) {
169
+ if (elementsAreInFrameBounds(elementsInGroup, frame, elementsMap)) {
158
170
  for (const element of elementsInGroup) {
159
171
  nextElementsInFrame.add(element);
160
172
  }
@@ -165,49 +177,91 @@ export const getElementsInResizingFrame = (allElements, frame, appState) => {
165
177
  return !(isTextElement(element) && element.containerId);
166
178
  });
167
179
  };
168
- export const getElementsInNewFrame = (allElements, frame) => {
169
- return omitGroupsContainingFrameLikes(allElements, getElementsCompletelyInFrame(allElements, frame));
180
+ export const getElementsInNewFrame = (elements, frame, elementsMap) => {
181
+ return omitGroupsContainingFrameLikes(elements, getElementsCompletelyInFrame(elements, frame, elementsMap));
170
182
  };
171
- export const getContainingFrame = (element,
172
- /**
173
- * Optionally an elements map, in case the elements aren't in the Scene yet.
174
- * Takes precedence over Scene elements, even if the element exists
175
- * in Scene elements and not the supplied elements map.
176
- */
177
- elementsMap) => {
178
- if (element.frameId) {
179
- if (elementsMap) {
180
- return (elementsMap.get(element.frameId) ||
181
- null);
182
- }
183
- return (Scene.getScene(element)?.getElement(element.frameId) || null);
183
+ export const getContainingFrame = (element, elementsMap) => {
184
+ if (!element.frameId) {
185
+ return null;
184
186
  }
185
- return null;
187
+ return (elementsMap.get(element.frameId) ||
188
+ null);
186
189
  };
187
190
  // --------------------------- Frame Operations -------------------------------
191
+ /** */
192
+ export const filterElementsEligibleAsFrameChildren = (elements, frame) => {
193
+ const otherFrames = new Set();
194
+ const elementsMap = arrayToMap(elements);
195
+ elements = omitGroupsContainingFrameLikes(elements);
196
+ for (const element of elements) {
197
+ if (isFrameLikeElement(element) && element.id !== frame.id) {
198
+ otherFrames.add(element.id);
199
+ }
200
+ }
201
+ const processedGroups = new Set();
202
+ const eligibleElements = [];
203
+ for (const element of elements) {
204
+ // don't add frames or their children
205
+ if (isFrameLikeElement(element) ||
206
+ (element.frameId && otherFrames.has(element.frameId))) {
207
+ continue;
208
+ }
209
+ if (element.groupIds.length) {
210
+ const shallowestGroupId = element.groupIds.at(-1);
211
+ if (!processedGroups.has(shallowestGroupId)) {
212
+ processedGroups.add(shallowestGroupId);
213
+ const groupElements = getElementsInGroup(elements, shallowestGroupId);
214
+ if (groupElements.some((el) => elementOverlapsWithFrame(el, frame, elementsMap))) {
215
+ for (const child of groupElements) {
216
+ eligibleElements.push(child);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ else {
222
+ const overlaps = elementOverlapsWithFrame(element, frame, elementsMap);
223
+ if (overlaps) {
224
+ eligibleElements.push(element);
225
+ }
226
+ }
227
+ }
228
+ return eligibleElements;
229
+ };
188
230
  /**
189
231
  * Retains (or repairs for target frame) the ordering invriant where children
190
232
  * elements come right before the parent frame:
191
233
  * [el, el, child, child, frame, el]
234
+ *
235
+ * @returns mutated allElements (same data structure)
192
236
  */
193
237
  export const addElementsToFrame = (allElements, elementsToAdd, frame) => {
194
- const { currTargetFrameChildrenMap } = allElements.reduce((acc, element, index) => {
238
+ const elementsMap = arrayToMap(allElements);
239
+ const currTargetFrameChildrenMap = new Map();
240
+ for (const element of allElements.values()) {
195
241
  if (element.frameId === frame.id) {
196
- acc.currTargetFrameChildrenMap.set(element.id, true);
242
+ currTargetFrameChildrenMap.set(element.id, true);
197
243
  }
198
- return acc;
199
- }, {
200
- currTargetFrameChildrenMap: new Map(),
201
- });
244
+ }
202
245
  const suppliedElementsToAddSet = new Set(elementsToAdd.map((el) => el.id));
203
246
  const finalElementsToAdd = [];
247
+ const otherFrames = new Set();
248
+ for (const element of elementsToAdd) {
249
+ if (isFrameLikeElement(element) && element.id !== frame.id) {
250
+ otherFrames.add(element.id);
251
+ }
252
+ }
204
253
  // - add bound text elements if not already in the array
205
254
  // - filter out elements that are already in the frame
206
255
  for (const element of omitGroupsContainingFrameLikes(allElements, elementsToAdd)) {
256
+ // don't add frames or their children
257
+ if (isFrameLikeElement(element) ||
258
+ (element.frameId && otherFrames.has(element.frameId))) {
259
+ continue;
260
+ }
207
261
  if (!currTargetFrameChildrenMap.has(element.id)) {
208
262
  finalElementsToAdd.push(element);
209
263
  }
210
- const boundTextElement = getBoundTextElement(element);
264
+ const boundTextElement = getBoundTextElement(element, elementsMap);
211
265
  if (boundTextElement &&
212
266
  !suppliedElementsToAddSet.has(boundTextElement.id) &&
213
267
  !currTargetFrameChildrenMap.has(boundTextElement.id)) {
@@ -219,9 +273,9 @@ export const addElementsToFrame = (allElements, elementsToAdd, frame) => {
219
273
  frameId: frame.id,
220
274
  }, false);
221
275
  }
222
- return allElements.slice();
276
+ return allElements;
223
277
  };
224
- export const removeElementsFromFrame = (allElements, elementsToRemove, appState) => {
278
+ export const removeElementsFromFrame = (elementsToRemove, elementsMap) => {
225
279
  const _elementsToRemove = new Map();
226
280
  const toRemoveElementsByFrame = new Map();
227
281
  for (const element of elementsToRemove) {
@@ -229,7 +283,7 @@ export const removeElementsFromFrame = (allElements, elementsToRemove, appState)
229
283
  _elementsToRemove.set(element.id, element);
230
284
  const arr = toRemoveElementsByFrame.get(element.frameId) || [];
231
285
  arr.push(element);
232
- const boundTextElement = getBoundTextElement(element);
286
+ const boundTextElement = getBoundTextElement(element, elementsMap);
233
287
  if (boundTextElement) {
234
288
  _elementsToRemove.set(boundTextElement.id, boundTextElement);
235
289
  arr.push(boundTextElement);
@@ -242,14 +296,14 @@ export const removeElementsFromFrame = (allElements, elementsToRemove, appState)
242
296
  frameId: null,
243
297
  }, false);
244
298
  }
245
- return allElements.slice();
246
299
  };
247
- export const removeAllElementsFromFrame = (allElements, frame, appState) => {
300
+ export const removeAllElementsFromFrame = (allElements, frame) => {
248
301
  const elementsInFrame = getFrameChildren(allElements, frame.id);
249
- return removeElementsFromFrame(allElements, elementsInFrame, appState);
302
+ removeElementsFromFrame(elementsInFrame, arrayToMap(allElements));
303
+ return allElements;
250
304
  };
251
- export const replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame, appState) => {
252
- return addElementsToFrame(removeAllElementsFromFrame(allElements, frame, appState), nextElementsInFrame, frame);
305
+ export const replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame, app) => {
306
+ return addElementsToFrame(removeAllElementsFromFrame(allElements, frame), nextElementsInFrame, frame).slice();
253
307
  };
254
308
  /** does not mutate elements, but returns new ones */
255
309
  export const updateFrameMembershipOfSelectedElements = (allElements, appState, app) => {
@@ -272,16 +326,18 @@ export const updateFrameMembershipOfSelectedElements = (allElements, appState, a
272
326
  }
273
327
  }
274
328
  const elementsToRemove = new Set();
329
+ const elementsMap = arrayToMap(allElements);
275
330
  elementsToFilter.forEach((element) => {
276
331
  if (element.frameId &&
277
332
  !isFrameLikeElement(element) &&
278
- !isElementInFrame(element, allElements, appState)) {
333
+ !isElementInFrame(element, elementsMap, appState)) {
279
334
  elementsToRemove.add(element);
280
335
  }
281
336
  });
282
- return elementsToRemove.size > 0
283
- ? removeElementsFromFrame(allElements, [...elementsToRemove], appState)
284
- : allElements;
337
+ if (elementsToRemove.size > 0) {
338
+ removeElementsFromFrame(elementsToRemove, elementsMap);
339
+ }
340
+ return allElements;
285
341
  };
286
342
  /**
287
343
  * filters out elements that are inside groups that contain a frame element
@@ -293,7 +349,8 @@ export const omitGroupsContainingFrameLikes = (allElements,
293
349
  */
294
350
  selectedElements) => {
295
351
  const uniqueGroupIds = new Set();
296
- for (const el of selectedElements || allElements) {
352
+ const elements = selectedElements || allElements;
353
+ for (const el of elements.values()) {
297
354
  const topMostGroupId = el.groupIds[el.groupIds.length - 1];
298
355
  if (topMostGroupId) {
299
356
  uniqueGroupIds.add(topMostGroupId);
@@ -305,27 +362,33 @@ selectedElements) => {
305
362
  rejectedGroupIds.add(groupId);
306
363
  }
307
364
  }
308
- return (selectedElements || allElements).filter((el) => !rejectedGroupIds.has(el.groupIds[el.groupIds.length - 1]));
365
+ const ret = [];
366
+ for (const element of elements.values()) {
367
+ if (!rejectedGroupIds.has(element.groupIds[element.groupIds.length - 1])) {
368
+ ret.push(element);
369
+ }
370
+ }
371
+ return ret;
309
372
  };
310
373
  /**
311
374
  * depending on the appState, return target frame, which is the frame the given element
312
375
  * is going to be added to or remove from
313
376
  */
314
- export const getTargetFrame = (element, appState) => {
377
+ export const getTargetFrame = (element, elementsMap, appState) => {
315
378
  const _element = isTextElement(element)
316
- ? getContainerElement(element) || element
379
+ ? getContainerElement(element, elementsMap) || element
317
380
  : element;
318
381
  return appState.selectedElementIds[_element.id] &&
319
382
  appState.selectedElementsAreBeingDragged
320
383
  ? appState.frameToHighlight
321
- : getContainingFrame(_element);
384
+ : getContainingFrame(_element, elementsMap);
322
385
  };
323
386
  // TODO: this a huge bottleneck for large scenes, optimise
324
387
  // given an element, return if the element is in some frame
325
- export const isElementInFrame = (element, allElements, appState) => {
326
- const frame = getTargetFrame(element, appState);
388
+ export const isElementInFrame = (element, allElementsMap, appState) => {
389
+ const frame = getTargetFrame(element, allElementsMap, appState);
327
390
  const _element = isTextElement(element)
328
- ? getContainerElement(element) || element
391
+ ? getContainerElement(element, allElementsMap) || element
329
392
  : element;
330
393
  if (frame) {
331
394
  // Perf improvement:
@@ -337,11 +400,11 @@ export const isElementInFrame = (element, allElements, appState) => {
337
400
  return true;
338
401
  }
339
402
  if (_element.groupIds.length === 0) {
340
- return elementOverlapsWithFrame(_element, frame);
403
+ return elementOverlapsWithFrame(_element, frame, allElementsMap);
341
404
  }
342
- const allElementsInGroup = new Set(_element.groupIds.flatMap((gid) => getElementsInGroup(allElements, gid)));
405
+ const allElementsInGroup = new Set(_element.groupIds.flatMap((gid) => getElementsInGroup(allElementsMap, gid)));
343
406
  if (appState.editingGroupId && appState.selectedElementsAreBeingDragged) {
344
- const selectedElements = new Set(getSelectedElements(allElements, appState));
407
+ const selectedElements = new Set(getSelectedElements(allElementsMap, appState));
345
408
  const editingGroupOverlapsFrame = appState.frameToHighlight !== null;
346
409
  if (editingGroupOverlapsFrame) {
347
410
  return true;
@@ -356,7 +419,7 @@ export const isElementInFrame = (element, allElements, appState) => {
356
419
  }
357
420
  }
358
421
  for (const elementInGroup of allElementsInGroup) {
359
- if (elementOverlapsWithFrame(elementInGroup, frame)) {
422
+ if (elementOverlapsWithFrame(elementInGroup, frame, allElementsMap)) {
360
423
  return true;
361
424
  }
362
425
  }
@@ -364,10 +427,20 @@ export const isElementInFrame = (element, allElements, appState) => {
364
427
  return false;
365
428
  };
366
429
  export const getFrameLikeTitle = (element, frameIdx) => {
367
- const existingName = element.name?.trim();
368
- if (existingName) {
369
- return existingName;
370
- }
371
- // TODO name frames AI only is specific to AI frames
372
- return isFrameElement(element) ? `Frame ${frameIdx}` : `AI Frame ${frameIdx}`;
430
+ // TODO name frames "AI" only if specific to AI frames
431
+ return element.name === null
432
+ ? isFrameElement(element)
433
+ ? `Frame ${frameIdx}`
434
+ : `AI Frame $${frameIdx}`
435
+ : element.name;
436
+ };
437
+ export const getElementsOverlappingFrame = (elements, frame) => {
438
+ return (elementsOverlappingBBox({
439
+ elements,
440
+ bounds: frame,
441
+ type: "overlap",
442
+ })
443
+ // removes elements who are overlapping, but are in a different frame,
444
+ // and thus invisible in target frame
445
+ .filter((el) => !el.frameId || el.frameId === frame.id));
373
446
  };
@@ -1,4 +1,4 @@
1
- import { GroupId, ExcalidrawElement, NonDeleted, NonDeletedExcalidrawElement } from "./element/types";
1
+ import { GroupId, ExcalidrawElement, NonDeleted, NonDeletedExcalidrawElement, ElementsMapOrArray, ElementsMap } from "./element/types";
2
2
  import { AppClassProperties, AppState, InteractiveCanvasAppState } from "./types";
3
3
  import { Mutable } from "./utility-types";
4
4
  export declare const selectGroup: (groupId: GroupId, appState: InteractiveCanvasAppState, elements: readonly NonDeleted<ExcalidrawElement>[]) => Pick<InteractiveCanvasAppState, "selectedGroupIds" | "selectedElementIds" | "editingGroupId">;
@@ -18,7 +18,7 @@ export declare const selectGroupsFromGivenElements: (elements: readonly NonDelet
18
18
  };
19
19
  export declare const editGroupForSelectedElement: (appState: AppState, element: NonDeleted<ExcalidrawElement>) => AppState;
20
20
  export declare const isElementInGroup: (element: ExcalidrawElement, groupId: string) => boolean;
21
- export declare const getElementsInGroup: (elements: readonly ExcalidrawElement[], groupId: string) => ExcalidrawElement[];
21
+ export declare const getElementsInGroup: (elements: ElementsMapOrArray, groupId: string) => ExcalidrawElement[];
22
22
  export declare const getSelectedGroupIdForElement: (element: ExcalidrawElement, selectedGroupIds: {
23
23
  [groupId: string]: boolean;
24
24
  }) => string | undefined;
@@ -27,5 +27,5 @@ export declare const addToGroup: (prevGroupIds: ExcalidrawElement["groupIds"], n
27
27
  export declare const removeFromSelectedGroups: (groupIds: ExcalidrawElement["groupIds"], selectedGroupIds: {
28
28
  [groupId: string]: boolean;
29
29
  }) => string[];
30
- export declare const getMaximumGroups: (elements: ExcalidrawElement[]) => ExcalidrawElement[][];
30
+ export declare const getMaximumGroups: (elements: ExcalidrawElement[], elementsMap: ElementsMap) => ExcalidrawElement[][];
31
31
  export declare const elementsAreInSameGroup: (elements: ExcalidrawElement[]) => boolean;
@@ -171,7 +171,15 @@ export const editGroupForSelectedElement = (appState, element) => {
171
171
  };
172
172
  };
173
173
  export const isElementInGroup = (element, groupId) => element.groupIds.includes(groupId);
174
- export const getElementsInGroup = (elements, groupId) => elements.filter((element) => isElementInGroup(element, groupId));
174
+ export const getElementsInGroup = (elements, groupId) => {
175
+ const elementsInGroup = [];
176
+ for (const element of elements.values()) {
177
+ if (isElementInGroup(element, groupId)) {
178
+ elementsInGroup.push(element);
179
+ }
180
+ }
181
+ return elementsInGroup;
182
+ };
175
183
  export const getSelectedGroupIdForElement = (element, selectedGroupIds) => element.groupIds.find((groupId) => selectedGroupIds[groupId]);
176
184
  export const getNewGroupIdsForDuplication = (groupIds, editingGroupId, mapper) => {
177
185
  const copy = [...groupIds];
@@ -195,7 +203,7 @@ export const addToGroup = (prevGroupIds, newGroupId, editingGroupId) => {
195
203
  return groupIds;
196
204
  };
197
205
  export const removeFromSelectedGroups = (groupIds, selectedGroupIds) => groupIds.filter((groupId) => !selectedGroupIds[groupId]);
198
- export const getMaximumGroups = (elements) => {
206
+ export const getMaximumGroups = (elements, elementsMap) => {
199
207
  const groups = new Map();
200
208
  elements.forEach((element) => {
201
209
  const groupId = element.groupIds.length === 0
@@ -203,7 +211,7 @@ export const getMaximumGroups = (elements) => {
203
211
  : element.groupIds[element.groupIds.length - 1];
204
212
  const currentGroupMembers = groups.get(groupId) || [];
205
213
  // Include bound text if present when grouping
206
- const boundTextElement = getBoundTextElement(element);
214
+ const boundTextElement = getBoundTextElement(element, elementsMap);
207
215
  if (boundTextElement) {
208
216
  currentGroupMembers.push(boundTextElement);
209
217
  }
@@ -14,7 +14,7 @@ declare const clearAppStatePropertiesForHistory: (appState: AppState) => {
14
14
  viewBackgroundColor: string;
15
15
  editingLinearElement: import("./element/linearElementEditor").LinearElementEditor | null;
16
16
  editingGroupId: string | null;
17
- name: string;
17
+ name: string | null;
18
18
  };
19
19
  declare class History {
20
20
  private elementCache;
@@ -2,7 +2,7 @@ import { atom, useAtom } from "jotai";
2
2
  import { useEffect, useState } from "react";
3
3
  import { COLOR_PALETTE } from "../colors";
4
4
  import { jotaiScope } from "../jotai";
5
- import { exportToSvg } from "../../utils/index";
5
+ import { exportToSvg } from "../../utils/export";
6
6
  export const libraryItemSvgsCache = atom(new Map());
7
7
  const exportLibraryItemToSvg = async (elements) => {
8
8
  return await exportToSvg({