@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,7 +1,6 @@
1
- import { getFontString, arrayToMap, isTestEnv } from "../utils";
1
+ import { getFontString, arrayToMap, isTestEnv, normalizeEOL } from "../utils";
2
2
  import { mutateElement } from "./mutateElement";
3
- import { ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, ARROW_LABEL_WIDTH_FRACTION, BOUND_TEXT_PADDING, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, FONT_FAMILY, isSafari, TEXT_ALIGN, VERTICAL_ALIGN, } from "../constants";
4
- import Scene from "../scene/Scene";
3
+ import { ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, ARROW_LABEL_WIDTH_FRACTION, BOUND_TEXT_PADDING, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, FONT_FAMILY, TEXT_ALIGN, VERTICAL_ALIGN, } from "../constants";
5
4
  import { isTextElement } from ".";
6
5
  import { isBoundToContainer, isArrowElement } from "./typeChecks";
7
6
  import { LinearElementEditor } from "./linearElementEditor";
@@ -9,18 +8,16 @@ import { isTextBindableContainer } from "./typeChecks";
9
8
  import { getElementAbsoluteCoords } from ".";
10
9
  import { getSelectedElements } from "../scene";
11
10
  import { isHittingElementNotConsideringBoundingBox } from "./collision";
12
- import { resetOriginalContainerCache, updateOriginalContainerCache, } from "./textWysiwyg";
11
+ import { resetOriginalContainerCache, updateOriginalContainerCache, } from "./containerCache";
13
12
  export const normalizeText = (text) => {
14
- return (text
13
+ return (normalizeEOL(text)
15
14
  // replace tabs with spaces so they render and measure correctly
16
- .replace(/\t/g, " ")
17
- // normalize newlines
18
- .replace(/\r?\n|\r/g, "\n"));
15
+ .replace(/\t/g, " "));
19
16
  };
20
- export const splitIntoLines = (text) => {
17
+ const splitIntoLines = (text) => {
21
18
  return normalizeText(text).split("\n");
22
19
  };
23
- export const redrawTextBoundingBox = (textElement, container) => {
20
+ export const redrawTextBoundingBox = (textElement, container, elementsMap) => {
24
21
  let maxWidth = undefined;
25
22
  const boundTextUpdates = {
26
23
  x: textElement.x,
@@ -28,7 +25,6 @@ export const redrawTextBoundingBox = (textElement, container) => {
28
25
  text: textElement.text,
29
26
  width: textElement.width,
30
27
  height: textElement.height,
31
- baseline: textElement.baseline,
32
28
  };
33
29
  boundTextUpdates.text = textElement.text;
34
30
  if (container) {
@@ -38,10 +34,9 @@ export const redrawTextBoundingBox = (textElement, container) => {
38
34
  const metrics = measureText(boundTextUpdates.text, getFontString(textElement), textElement.lineHeight);
39
35
  boundTextUpdates.width = metrics.width;
40
36
  boundTextUpdates.height = metrics.height;
41
- boundTextUpdates.baseline = metrics.baseline;
42
37
  if (container) {
43
38
  const maxContainerHeight = getBoundTextMaxHeight(container, textElement);
44
- const maxContainerWidth = getBoundTextMaxWidth(container);
39
+ const maxContainerWidth = getBoundTextMaxWidth(container, textElement);
45
40
  if (!isArrowElement(container) && metrics.height > maxContainerHeight) {
46
41
  const nextHeight = computeContainerDimensionForBoundText(metrics.height, container.type);
47
42
  mutateElement(container, { height: nextHeight });
@@ -55,21 +50,21 @@ export const redrawTextBoundingBox = (textElement, container) => {
55
50
  ...textElement,
56
51
  ...boundTextUpdates,
57
52
  };
58
- const { x, y } = computeBoundTextPosition(container, updatedTextElement);
53
+ const { x, y } = computeBoundTextPosition(container, updatedTextElement, elementsMap);
59
54
  boundTextUpdates.x = x;
60
55
  boundTextUpdates.y = y;
61
56
  }
62
57
  mutateElement(textElement, boundTextUpdates);
63
58
  };
64
- export const bindTextToShapeAfterDuplication = (sceneElements, oldElements, oldIdToDuplicatedId) => {
65
- const sceneElementMap = arrayToMap(sceneElements);
59
+ export const bindTextToShapeAfterDuplication = (newElements, oldElements, oldIdToDuplicatedId) => {
60
+ const newElementsMap = arrayToMap(newElements);
66
61
  oldElements.forEach((element) => {
67
62
  const newElementId = oldIdToDuplicatedId.get(element.id);
68
63
  const boundTextElementId = getBoundTextElementId(element);
69
64
  if (boundTextElementId) {
70
65
  const newTextElementId = oldIdToDuplicatedId.get(boundTextElementId);
71
66
  if (newTextElementId) {
72
- const newContainer = sceneElementMap.get(newElementId);
67
+ const newContainer = newElementsMap.get(newElementId);
73
68
  if (newContainer) {
74
69
  mutateElement(newContainer, {
75
70
  boundElements: (element.boundElements || [])
@@ -81,7 +76,7 @@ export const bindTextToShapeAfterDuplication = (sceneElements, oldElements, oldI
81
76
  }),
82
77
  });
83
78
  }
84
- const newTextElement = sceneElementMap.get(newTextElementId);
79
+ const newTextElement = newElementsMap.get(newTextElementId);
85
80
  if (newTextElement && isTextElement(newTextElement)) {
86
81
  mutateElement(newTextElement, {
87
82
  containerId: newContainer ? newElementId : null,
@@ -91,25 +86,23 @@ export const bindTextToShapeAfterDuplication = (sceneElements, oldElements, oldI
91
86
  }
92
87
  });
93
88
  };
94
- export const handleBindTextResize = (container, transformHandleType, shouldMaintainAspectRatio = false) => {
89
+ export const handleBindTextResize = (container, elementsMap, transformHandleType, shouldMaintainAspectRatio = false) => {
95
90
  const boundTextElementId = getBoundTextElementId(container);
96
91
  if (!boundTextElementId) {
97
92
  return;
98
93
  }
99
94
  resetOriginalContainerCache(container.id);
100
- let textElement = Scene.getScene(container).getElement(boundTextElementId);
95
+ const textElement = getBoundTextElement(container, elementsMap);
101
96
  if (textElement && textElement.text) {
102
97
  if (!container) {
103
98
  return;
104
99
  }
105
- textElement = Scene.getScene(container).getElement(boundTextElementId);
106
100
  let text = textElement.text;
107
101
  let nextHeight = textElement.height;
108
102
  let nextWidth = textElement.width;
109
- const maxWidth = getBoundTextMaxWidth(container);
103
+ const maxWidth = getBoundTextMaxWidth(container, textElement);
110
104
  const maxHeight = getBoundTextMaxHeight(container, textElement);
111
105
  let containerHeight = container.height;
112
- let nextBaseLine = textElement.baseline;
113
106
  if (shouldMaintainAspectRatio ||
114
107
  (transformHandleType !== "n" && transformHandleType !== "s")) {
115
108
  if (text) {
@@ -118,7 +111,6 @@ export const handleBindTextResize = (container, transformHandleType, shouldMaint
118
111
  const metrics = measureText(text, getFontString(textElement), textElement.lineHeight);
119
112
  nextHeight = metrics.height;
120
113
  nextWidth = metrics.width;
121
- nextBaseLine = metrics.baseline;
122
114
  }
123
115
  // increase height in case text element height exceeds
124
116
  if (nextHeight > maxHeight) {
@@ -140,20 +132,19 @@ export const handleBindTextResize = (container, transformHandleType, shouldMaint
140
132
  text,
141
133
  width: nextWidth,
142
134
  height: nextHeight,
143
- baseline: nextBaseLine,
144
135
  });
145
136
  if (!isArrowElement(container)) {
146
- mutateElement(textElement, computeBoundTextPosition(container, textElement));
137
+ mutateElement(textElement, computeBoundTextPosition(container, textElement, elementsMap));
147
138
  }
148
139
  }
149
140
  };
150
- export const computeBoundTextPosition = (container, boundTextElement) => {
141
+ export const computeBoundTextPosition = (container, boundTextElement, elementsMap) => {
151
142
  if (isArrowElement(container)) {
152
- return LinearElementEditor.getBoundTextElementPosition(container, boundTextElement);
143
+ return LinearElementEditor.getBoundTextElementPosition(container, boundTextElement, elementsMap);
153
144
  }
154
145
  const containerCoords = getContainerCoords(container);
155
146
  const maxContainerHeight = getBoundTextMaxHeight(container, boundTextElement);
156
- const maxContainerWidth = getBoundTextMaxWidth(container);
147
+ const maxContainerWidth = getBoundTextMaxWidth(container, boundTextElement);
157
148
  let x;
158
149
  let y;
159
150
  if (boundTextElement.verticalAlign === VERTICAL_ALIGN.TOP) {
@@ -179,7 +170,6 @@ export const computeBoundTextPosition = (container, boundTextElement) => {
179
170
  }
180
171
  return { x, y };
181
172
  };
182
- // https://github.com/grassator/canvas-text-editor/blob/master/lib/FontMetrics.js
183
173
  export const measureText = (text, font, lineHeight) => {
184
174
  text = text
185
175
  .split("\n")
@@ -190,47 +180,7 @@ export const measureText = (text, font, lineHeight) => {
190
180
  const fontSize = parseFloat(font);
191
181
  const height = getTextHeight(text, fontSize, lineHeight);
192
182
  const width = getTextWidth(text, font);
193
- const baseline = measureBaseline(text, font, lineHeight);
194
- return { width, height, baseline };
195
- };
196
- export const measureBaseline = (text, font, lineHeight, wrapInContainer) => {
197
- const container = document.createElement("div");
198
- container.style.position = "absolute";
199
- container.style.whiteSpace = "pre";
200
- container.style.font = font;
201
- container.style.minHeight = "1em";
202
- if (wrapInContainer) {
203
- container.style.overflow = "hidden";
204
- container.style.wordBreak = "break-word";
205
- container.style.whiteSpace = "pre-wrap";
206
- }
207
- container.style.lineHeight = String(lineHeight);
208
- container.innerText = text;
209
- // Baseline is important for positioning text on canvas
210
- document.body.appendChild(container);
211
- const span = document.createElement("span");
212
- span.style.display = "inline-block";
213
- span.style.overflow = "hidden";
214
- span.style.width = "1px";
215
- span.style.height = "1px";
216
- container.appendChild(span);
217
- let baseline = span.offsetTop + span.offsetHeight;
218
- const height = container.offsetHeight;
219
- if (isSafari) {
220
- const canvasHeight = getTextHeight(text, parseFloat(font), lineHeight);
221
- const fontSize = parseFloat(font);
222
- // In Safari the font size gets rounded off when rendering hence calculating the safari height and shifting the baseline if it differs
223
- // from the actual canvas height
224
- const domHeight = getTextHeight(text, Math.round(fontSize), lineHeight);
225
- if (canvasHeight > height) {
226
- baseline += canvasHeight - domHeight;
227
- }
228
- if (height > canvasHeight) {
229
- baseline -= domHeight - canvasHeight;
230
- }
231
- }
232
- document.body.removeChild(container);
233
- return baseline;
183
+ return { width, height };
234
184
  };
235
185
  /**
236
186
  * To get unitless line-height (if unknown) we can calculate it by dividing
@@ -249,6 +199,16 @@ export const detectLineHeight = (textElement) => {
249
199
  export const getLineHeightInPx = (fontSize, lineHeight) => {
250
200
  return fontSize * lineHeight;
251
201
  };
202
+ /**
203
+ * Calculates vertical offset for a text with alphabetic baseline.
204
+ */
205
+ export const getVerticalOffset = (fontFamily, fontSize, lineHeightPx) => {
206
+ const { unitsPerEm, ascender, descender } = FONT_METRICS[fontFamily];
207
+ const fontSizeEm = fontSize / unitsPerEm;
208
+ const lineGap = lineHeightPx - fontSizeEm * ascender + fontSizeEm * descender;
209
+ const verticalOffset = fontSizeEm * ascender + lineGap;
210
+ return verticalOffset;
211
+ };
252
212
  // FIXME rename to getApproxMinContainerHeight
253
213
  export const getApproxMinLineHeight = (fontSize, lineHeight) => {
254
214
  return getLineHeightInPx(fontSize, lineHeight) + BOUND_TEXT_PADDING * 2;
@@ -486,42 +446,44 @@ export const getBoundTextElementId = (container) => {
486
446
  null
487
447
  : null;
488
448
  };
489
- export const getBoundTextElement = (element) => {
449
+ export const getBoundTextElement = (element, elementsMap) => {
490
450
  if (!element) {
491
451
  return null;
492
452
  }
493
453
  const boundTextElementId = getBoundTextElementId(element);
494
454
  if (boundTextElementId) {
495
- return (Scene.getScene(element)?.getElement(boundTextElementId) || null);
455
+ return (elementsMap.get(boundTextElementId) ||
456
+ null);
496
457
  }
497
458
  return null;
498
459
  };
499
- export const getContainerElement = (element) => {
460
+ export const getContainerElement = (element, elementsMap) => {
500
461
  if (!element) {
501
462
  return null;
502
463
  }
503
464
  if (element.containerId) {
504
- return Scene.getScene(element)?.getElement(element.containerId) || null;
465
+ return (elementsMap.get(element.containerId) ||
466
+ null);
505
467
  }
506
468
  return null;
507
469
  };
508
- export const getContainerCenter = (container, appState) => {
470
+ export const getContainerCenter = (container, appState, elementsMap) => {
509
471
  if (!isArrowElement(container)) {
510
472
  return {
511
473
  x: container.x + container.width / 2,
512
474
  y: container.y + container.height / 2,
513
475
  };
514
476
  }
515
- const points = LinearElementEditor.getPointsGlobalCoordinates(container);
477
+ const points = LinearElementEditor.getPointsGlobalCoordinates(container, elementsMap);
516
478
  if (points.length % 2 === 1) {
517
479
  const index = Math.floor(container.points.length / 2);
518
- const midPoint = LinearElementEditor.getPointGlobalCoordinates(container, container.points[index]);
480
+ const midPoint = LinearElementEditor.getPointGlobalCoordinates(container, container.points[index], elementsMap);
519
481
  return { x: midPoint[0], y: midPoint[1] };
520
482
  }
521
483
  const index = container.points.length / 2 - 1;
522
- let midSegmentMidpoint = LinearElementEditor.getEditorMidPoints(container, appState)[index];
484
+ let midSegmentMidpoint = LinearElementEditor.getEditorMidPoints(container, elementsMap, appState)[index];
523
485
  if (!midSegmentMidpoint) {
524
- midSegmentMidpoint = LinearElementEditor.getSegmentMidPoint(container, points[index], points[index + 1], index + 1);
486
+ midSegmentMidpoint = LinearElementEditor.getSegmentMidPoint(container, points[index], points[index + 1], index + 1, elementsMap);
525
487
  }
526
488
  return { x: midSegmentMidpoint[0], y: midSegmentMidpoint[1] };
527
489
  };
@@ -543,34 +505,22 @@ export const getContainerCoords = (container) => {
543
505
  y: container.y + offsetY,
544
506
  };
545
507
  };
546
- export const getTextElementAngle = (textElement) => {
547
- const container = getContainerElement(textElement);
508
+ export const getTextElementAngle = (textElement, container) => {
548
509
  if (!container || isArrowElement(container)) {
549
510
  return textElement.angle;
550
511
  }
551
512
  return container.angle;
552
513
  };
553
- export const getBoundTextElementOffset = (boundTextElement) => {
554
- const container = getContainerElement(boundTextElement);
555
- if (!container || !boundTextElement) {
556
- return 0;
557
- }
558
- if (isArrowElement(container)) {
559
- return BOUND_TEXT_PADDING * 8;
560
- }
561
- return BOUND_TEXT_PADDING;
562
- };
563
- export const getBoundTextElementPosition = (container, boundTextElement) => {
514
+ export const getBoundTextElementPosition = (container, boundTextElement, elementsMap) => {
564
515
  if (isArrowElement(container)) {
565
- return LinearElementEditor.getBoundTextElementPosition(container, boundTextElement);
516
+ return LinearElementEditor.getBoundTextElementPosition(container, boundTextElement, elementsMap);
566
517
  }
567
518
  };
568
- export const shouldAllowVerticalAlign = (selectedElements) => {
519
+ export const shouldAllowVerticalAlign = (selectedElements, elementsMap) => {
569
520
  return selectedElements.some((element) => {
570
- const hasBoundContainer = isBoundToContainer(element);
571
- if (hasBoundContainer) {
572
- const container = getContainerElement(element);
573
- if (isTextElement(element) && isArrowElement(container)) {
521
+ if (isBoundToContainer(element)) {
522
+ const container = getContainerElement(element, elementsMap);
523
+ if (isArrowElement(container)) {
574
524
  return false;
575
525
  }
576
526
  return true;
@@ -578,12 +528,11 @@ export const shouldAllowVerticalAlign = (selectedElements) => {
578
528
  return false;
579
529
  });
580
530
  };
581
- export const suppportsHorizontalAlign = (selectedElements) => {
531
+ export const suppportsHorizontalAlign = (selectedElements, elementsMap) => {
582
532
  return selectedElements.some((element) => {
583
- const hasBoundContainer = isBoundToContainer(element);
584
- if (hasBoundContainer) {
585
- const container = getContainerElement(element);
586
- if (isTextElement(element) && isArrowElement(container)) {
533
+ if (isBoundToContainer(element)) {
534
+ const container = getContainerElement(element, elementsMap);
535
+ if (isArrowElement(container)) {
587
536
  return false;
588
537
  }
589
538
  return true;
@@ -591,7 +540,7 @@ export const suppportsHorizontalAlign = (selectedElements) => {
591
540
  return isTextElement(element);
592
541
  });
593
542
  };
594
- export const getTextBindableContainerAtPosition = (elements, appState, x, y) => {
543
+ export const getTextBindableContainerAtPosition = (elements, appState, x, y, elementsMap) => {
595
544
  const selectedElements = getSelectedElements(elements, appState);
596
545
  if (selectedElements.length === 1) {
597
546
  return isTextBindableContainer(selectedElements[0], false)
@@ -604,9 +553,9 @@ export const getTextBindableContainerAtPosition = (elements, appState, x, y) =>
604
553
  if (elements[index].isDeleted) {
605
554
  continue;
606
555
  }
607
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[index]);
556
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[index], elementsMap);
608
557
  if (isArrowElement(elements[index]) &&
609
- isHittingElementNotConsideringBoundingBox(elements[index], appState, null, [x, y])) {
558
+ isHittingElementNotConsideringBoundingBox(elements[index], appState, null, [x, y], elementsMap)) {
610
559
  hitElement = elements[index];
611
560
  break;
612
561
  }
@@ -638,7 +587,7 @@ export const computeContainerDimensionForBoundText = (dimension, containerType)
638
587
  }
639
588
  return dimension + padding;
640
589
  };
641
- export const getBoundTextMaxWidth = (container, boundTextElement = getBoundTextElement(container)) => {
590
+ export const getBoundTextMaxWidth = (container, boundTextElement) => {
642
591
  const { width } = container;
643
592
  if (isArrowElement(container)) {
644
593
  const minWidth = (boundTextElement?.fontSize ?? DEFAULT_FONT_SIZE) *
@@ -702,12 +651,32 @@ const DEFAULT_LINE_HEIGHT = {
702
651
  // ~1.25 is the average for Virgil in WebKit and Blink.
703
652
  // Gecko (FF) uses ~1.28.
704
653
  [FONT_FAMILY.Virgil]: 1.25,
705
- // ~1.15 is the average for Virgil in WebKit and Blink.
706
- // Gecko if all over the place.
654
+ // ~1.15 is the average for Helvetica in WebKit and Blink.
707
655
  [FONT_FAMILY.Helvetica]: 1.15,
708
- // ~1.2 is the average for Virgil in WebKit and Blink, and kinda Gecko too
656
+ // ~1.2 is the average for Cascadia in WebKit and Blink, and kinda Gecko too
709
657
  [FONT_FAMILY.Cascadia]: 1.2,
710
658
  };
659
+ /**
660
+ * Hardcoded metrics for default fonts, read by https://opentype.js.org/font-inspector.html.
661
+ * For custom fonts, read these metrics on load and extend this object.
662
+ */
663
+ const FONT_METRICS = {
664
+ [FONT_FAMILY.Virgil]: {
665
+ unitsPerEm: 1000,
666
+ ascender: 886,
667
+ descender: -374,
668
+ },
669
+ [FONT_FAMILY.Helvetica]: {
670
+ unitsPerEm: 2048,
671
+ ascender: 1577,
672
+ descender: -471,
673
+ },
674
+ [FONT_FAMILY.Cascadia]: {
675
+ unitsPerEm: 2048,
676
+ ascender: 1977,
677
+ descender: -480,
678
+ },
679
+ };
711
680
  export const getDefaultLineHeight = (fontFamily) => {
712
681
  if (fontFamily in DEFAULT_LINE_HEIGHT) {
713
682
  return DEFAULT_LINE_HEIGHT[fontFamily];
@@ -1,10 +1,5 @@
1
- import { ExcalidrawElement, ExcalidrawTextElement, ExcalidrawTextContainer } from "./types";
1
+ import { ExcalidrawElement, ExcalidrawTextElement } from "./types";
2
2
  import App from "../components/App";
3
- export declare const updateOriginalContainerCache: (id: ExcalidrawTextContainer["id"], height: ExcalidrawTextContainer["height"]) => {
4
- height: ExcalidrawTextContainer["height"];
5
- };
6
- export declare const resetOriginalContainerCache: (id: ExcalidrawTextContainer["id"]) => void;
7
- export declare const getOriginalContainerHeightFromCache: (id: ExcalidrawTextContainer["id"]) => number | null;
8
3
  export declare const textWysiwyg: ({ id, onChange, onSubmit, getViewportCoords, element, canvas, excalidrawContainer, app, }: {
9
4
  id: ExcalidrawElement["id"];
10
5
  onChange?: ((text: string) => void) | undefined;
@@ -2,13 +2,14 @@ import { CODES, KEYS } from "../keys";
2
2
  import { isWritableElement, getFontString, getFontFamilyString, isTestEnv, } from "../utils";
3
3
  import Scene from "../scene/Scene";
4
4
  import { isArrowElement, isBoundToContainer, isTextElement, } from "./typeChecks";
5
- import { CLASSES, isSafari } from "../constants";
5
+ import { CLASSES } from "../constants";
6
6
  import { bumpVersion, mutateElement } from "./mutateElement";
7
- import { getBoundTextElementId, getContainerElement, getTextElementAngle, getTextWidth, normalizeText, redrawTextBoundingBox, wrapText, getBoundTextMaxHeight, getBoundTextMaxWidth, computeContainerDimensionForBoundText, detectLineHeight, computeBoundTextPosition, } from "./textElement";
7
+ import { getBoundTextElementId, getContainerElement, getTextElementAngle, getTextWidth, normalizeText, redrawTextBoundingBox, wrapText, getBoundTextMaxHeight, getBoundTextMaxWidth, computeContainerDimensionForBoundText, computeBoundTextPosition, getBoundTextElement, } from "./textElement";
8
8
  import { actionDecreaseFontSize, actionIncreaseFontSize, } from "../actions/actionProperties";
9
9
  import { actionZoomIn, actionZoomOut } from "../actions/actionCanvas";
10
10
  import { LinearElementEditor } from "./linearElementEditor";
11
11
  import { parseClipboard } from "../clipboard";
12
+ import { originalContainerCache, updateOriginalContainerCache, } from "./containerCache";
12
13
  const getTransform = (width, height, angle, appState, maxWidth, maxHeight) => {
13
14
  const { zoom } = appState;
14
15
  const degree = (180 * angle) / Math.PI;
@@ -22,20 +23,6 @@ const getTransform = (width, height, angle, appState, maxWidth, maxHeight) => {
22
23
  }
23
24
  return `translate(${translateX}px, ${translateY}px) scale(${zoom.value}) rotate(${degree}deg)`;
24
25
  };
25
- const originalContainerCache = {};
26
- export const updateOriginalContainerCache = (id, height) => {
27
- const data = originalContainerCache[id] || (originalContainerCache[id] = { height });
28
- data.height = height;
29
- return data;
30
- };
31
- export const resetOriginalContainerCache = (id) => {
32
- if (originalContainerCache[id]) {
33
- delete originalContainerCache[id];
34
- }
35
- };
36
- export const getOriginalContainerHeightFromCache = (id) => {
37
- return originalContainerCache[id]?.height ?? null;
38
- };
39
26
  export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element, canvas, excalidrawContainer, app, }) => {
40
27
  const textPropertiesUpdated = (updatedTextElement, editable) => {
41
28
  if (!editable.style.fontFamily || !editable.style.fontSize) {
@@ -58,10 +45,11 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
58
45
  return;
59
46
  }
60
47
  const { textAlign, verticalAlign } = updatedTextElement;
48
+ const elementsMap = app.scene.getNonDeletedElementsMap();
61
49
  if (updatedTextElement && isTextElement(updatedTextElement)) {
62
50
  let coordX = updatedTextElement.x;
63
51
  let coordY = updatedTextElement.y;
64
- const container = getContainerElement(updatedTextElement);
52
+ const container = getContainerElement(updatedTextElement, app.scene.getNonDeletedElementsMap());
65
53
  let maxWidth = updatedTextElement.width;
66
54
  let maxHeight = updatedTextElement.height;
67
55
  let textElementWidth = updatedTextElement.width;
@@ -70,7 +58,7 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
70
58
  const textElementHeight = updatedTextElement.height;
71
59
  if (container && updatedTextElement.containerId) {
72
60
  if (isArrowElement(container)) {
73
- const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, updatedTextElement);
61
+ const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, updatedTextElement, elementsMap);
74
62
  coordX = boundTextCoords.x;
75
63
  coordY = boundTextCoords.y;
76
64
  }
@@ -85,7 +73,7 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
85
73
  originalContainerData = updateOriginalContainerCache(container.id, container.height);
86
74
  }
87
75
  }
88
- maxWidth = getBoundTextMaxWidth(container);
76
+ maxWidth = getBoundTextMaxWidth(container, updatedTextElement);
89
77
  maxHeight = getBoundTextMaxHeight(container, updatedTextElement);
90
78
  // autogrow container height if text exceeds
91
79
  if (!isArrowElement(container) && textElementHeight > maxHeight) {
@@ -103,7 +91,7 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
103
91
  mutateElement(container, { height: targetContainerHeight });
104
92
  }
105
93
  else {
106
- const { y } = computeBoundTextPosition(container, updatedTextElement);
94
+ const { y } = computeBoundTextPosition(container, updatedTextElement, elementsMap);
107
95
  coordY = y;
108
96
  }
109
97
  }
@@ -125,28 +113,17 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
125
113
  maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;
126
114
  textElementWidth = Math.min(textElementWidth, maxWidth);
127
115
  }
128
- else {
129
- textElementWidth += 0.5;
130
- }
131
- let lineHeight = updatedTextElement.lineHeight;
132
- // In Safari the font size gets rounded off when rendering hence calculating the line height by rounding off font size
133
- if (isSafari) {
134
- lineHeight = detectLineHeight({
135
- ...updatedTextElement,
136
- fontSize: Math.round(updatedTextElement.fontSize),
137
- });
138
- }
139
116
  // Make sure text editor height doesn't go beyond viewport
140
117
  const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value;
141
118
  Object.assign(editable.style, {
142
119
  font: getFontString(updatedTextElement),
143
120
  // must be defined *after* font ¯\_(ツ)_/¯
144
- lineHeight,
121
+ lineHeight: updatedTextElement.lineHeight,
145
122
  width: `${textElementWidth}px`,
146
123
  height: `${textElementHeight}px`,
147
124
  left: `${viewportX}px`,
148
125
  top: `${viewportY}px`,
149
- transform: getTransform(textElementWidth, textElementHeight, getTextElementAngle(updatedTextElement), appState, maxWidth, editorMaxHeight),
126
+ transform: getTransform(textElementWidth, textElementHeight, getTextElementAngle(updatedTextElement, container), appState, maxWidth, editorMaxHeight),
150
127
  textAlign,
151
128
  verticalAlign,
152
129
  color: updatedTextElement.strokeColor,
@@ -208,13 +185,14 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
208
185
  if (!data) {
209
186
  return;
210
187
  }
211
- const container = getContainerElement(element);
188
+ const container = getContainerElement(element, app.scene.getNonDeletedElementsMap());
212
189
  const font = getFontString({
213
190
  fontSize: app.state.currentItemFontSize,
214
191
  fontFamily: app.state.currentItemFontFamily,
215
192
  });
216
193
  if (container) {
217
- const wrappedText = wrapText(`${editable.value}${data}`, font, getBoundTextMaxWidth(container));
194
+ const boundTextElement = getBoundTextElement(container, app.scene.getNonDeletedElementsMap());
195
+ const wrappedText = wrapText(`${editable.value}${data}`, font, getBoundTextMaxWidth(container, boundTextElement));
218
196
  const width = getTextWidth(wrappedText, font);
219
197
  editable.style.width = `${width}px`;
220
198
  }
@@ -357,7 +335,7 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
357
335
  return;
358
336
  }
359
337
  let text = editable.value;
360
- const container = getContainerElement(updateElement);
338
+ const container = getContainerElement(updateElement, app.scene.getNonDeletedElementsMap());
361
339
  if (container) {
362
340
  text = updateElement.text;
363
341
  if (editable.value.trim()) {
@@ -380,7 +358,7 @@ export const textWysiwyg = ({ id, onChange, onSubmit, getViewportCoords, element
380
358
  boundElements: container.boundElements?.filter((ele) => !isTextElement(ele)),
381
359
  });
382
360
  }
383
- redrawTextBoundingBox(updateElement, container);
361
+ redrawTextBoundingBox(updateElement, container, app.scene.getNonDeletedElementsMap());
384
362
  }
385
363
  onSubmit({
386
364
  text,
@@ -1,4 +1,4 @@
1
- import { ExcalidrawElement, NonDeletedExcalidrawElement, PointerType } from "./types";
1
+ import { ElementsMap, ExcalidrawElement, NonDeletedExcalidrawElement, PointerType } from "./types";
2
2
  import { Bounds } from "./bounds";
3
3
  import { InteractiveCanvasAppState, Zoom } from "../types";
4
4
  export type TransformHandleDirection = "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se";
@@ -23,14 +23,14 @@ export declare const OMIT_SIDES_FOR_FRAME: {
23
23
  };
24
24
  export declare const getTransformHandlesFromCoords: ([x1, y1, x2, y2, cx, cy]: [number, number, number, number, number, number], angle: number, zoom: Zoom, pointerType: PointerType, omitSides?: {
25
25
  s?: boolean | undefined;
26
- e?: boolean | undefined;
27
- w?: boolean | undefined;
28
26
  n?: boolean | undefined;
27
+ w?: boolean | undefined;
28
+ e?: boolean | undefined;
29
29
  nw?: boolean | undefined;
30
30
  ne?: boolean | undefined;
31
31
  sw?: boolean | undefined;
32
32
  se?: boolean | undefined;
33
33
  rotation?: boolean | undefined;
34
34
  }, margin?: number) => TransformHandles;
35
- export declare const getTransformHandles: (element: ExcalidrawElement, zoom: Zoom, pointerType?: PointerType) => TransformHandles;
35
+ export declare const getTransformHandles: (element: ExcalidrawElement, zoom: Zoom, elementsMap: ElementsMap, pointerType?: PointerType) => TransformHandles;
36
36
  export declare const shouldShowBoundingBox: (elements: readonly NonDeletedExcalidrawElement[], appState: InteractiveCanvasAppState) => boolean;
@@ -2,7 +2,7 @@ import { getElementAbsoluteCoords } from "./bounds";
2
2
  import { rotate } from "../math";
3
3
  import { isTextElement } from ".";
4
4
  import { isFrameLikeElement, isLinearElement } from "./typeChecks";
5
- import { DEFAULT_SPACING } from "../renderer/renderScene";
5
+ import { DEFAULT_TRANSFORM_HANDLE_SPACING } from "../constants";
6
6
  const transformHandleSizes = {
7
7
  mouse: 8,
8
8
  pen: 16,
@@ -55,7 +55,7 @@ export const getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, z
55
55
  const width = x2 - x1;
56
56
  const height = y2 - y1;
57
57
  const dashedLineMargin = margin / zoom.value;
58
- const centeringOffset = (size - DEFAULT_SPACING * 2) / (2 * zoom.value);
58
+ const centeringOffset = (size - DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / (2 * zoom.value);
59
59
  const transformHandles = {
60
60
  nw: omitSides.nw
61
61
  ? undefined
@@ -98,7 +98,7 @@ export const getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, z
98
98
  }
99
99
  return transformHandles;
100
100
  };
101
- export const getTransformHandles = (element, zoom, pointerType = "mouse") => {
101
+ export const getTransformHandles = (element, zoom, elementsMap, pointerType = "mouse") => {
102
102
  // so that when locked element is selected (especially when you toggle lock
103
103
  // via keyboard) the locked element is visually distinct, indicating
104
104
  // you can't move/resize
@@ -136,9 +136,9 @@ export const getTransformHandles = (element, zoom, pointerType = "mouse") => {
136
136
  };
137
137
  }
138
138
  const dashedLineMargin = isLinearElement(element)
139
- ? DEFAULT_SPACING + 8
140
- : DEFAULT_SPACING;
141
- return getTransformHandlesFromCoords(getElementAbsoluteCoords(element, true), element.angle, zoom, pointerType, omitSides, dashedLineMargin);
139
+ ? DEFAULT_TRANSFORM_HANDLE_SPACING + 8
140
+ : DEFAULT_TRANSFORM_HANDLE_SPACING;
141
+ return getTransformHandlesFromCoords(getElementAbsoluteCoords(element, elementsMap, true), element.angle, zoom, pointerType, omitSides, dashedLineMargin);
142
142
  };
143
143
  export const shouldShowBoundingBox = (elements, appState) => {
144
144
  if (appState.editingLinearElement) {
@@ -110,7 +110,10 @@ export const isBoundToContainer = (element) => {
110
110
  element.containerId !== null &&
111
111
  isTextElement(element));
112
112
  };
113
- export const isUsingAdaptiveRadius = (type) => type === "rectangle" || type === "embeddable" || type === "iframe";
113
+ export const isUsingAdaptiveRadius = (type) => type === "rectangle" ||
114
+ type === "embeddable" ||
115
+ type === "iframe" ||
116
+ type === "image";
114
117
  export const isUsingProportionalRadius = (type) => type === "line" || type === "arrow" || type === "diamond";
115
118
  export const canApplyRoundnessTypeToElement = (roundnessType, element) => {
116
119
  if ((roundnessType === ROUNDNESS.ADAPTIVE_RADIUS ||