@excalidraw/excalidraw 0.17.1-c0b80a0 → 0.17.1-c329470

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/browser/dev/excalidraw-assets-dev/{chunk-JGDL4H2X.js → chunk-3DLVY5XU.js} +8272 -6864
  2. package/dist/browser/dev/excalidraw-assets-dev/chunk-3DLVY5XU.js.map +7 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/{chunk-V7NFEZA6.js → chunk-NOAEU4NM.js} +9 -2
  4. package/dist/browser/dev/excalidraw-assets-dev/chunk-NOAEU4NM.js.map +7 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/{en-ZSVWGT55.js → en-7IBTMWBG.js} +2 -2
  6. package/dist/browser/dev/excalidraw-assets-dev/{image-RJG3J34Y.js → image-N5AC7SEK.js} +2 -6
  7. package/dist/browser/dev/index.css +85 -50
  8. package/dist/browser/dev/index.css.map +3 -3
  9. package/dist/browser/dev/index.js +4375 -3766
  10. package/dist/browser/dev/index.js.map +4 -4
  11. package/dist/browser/prod/excalidraw-assets/{chunk-LDVEIXGO.js → chunk-7CSIPVOW.js} +2 -2
  12. package/dist/browser/prod/excalidraw-assets/chunk-TX3BU7T2.js +47 -0
  13. package/dist/browser/prod/excalidraw-assets/{en-UPNEHLDS.js → en-LOGQBETY.js} +1 -1
  14. package/dist/browser/prod/excalidraw-assets/image-3V4U7GZE.js +1 -0
  15. package/dist/browser/prod/index.css +1 -1
  16. package/dist/browser/prod/index.js +40 -40
  17. package/dist/dev/index.css +85 -50
  18. package/dist/dev/index.css.map +3 -3
  19. package/dist/dev/index.js +8688 -6706
  20. package/dist/dev/index.js.map +4 -4
  21. package/dist/{prod/locales/en-ZXYG7GCR.json → dev/locales/en-V6KXFSCK.json} +8 -1
  22. package/dist/excalidraw/actions/actionAlign.d.ts +7 -6
  23. package/dist/excalidraw/actions/actionAlign.js +14 -14
  24. package/dist/excalidraw/actions/actionClipboard.d.ts +7 -3
  25. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +7 -3
  26. package/dist/excalidraw/actions/actionDeleteSelected.js +103 -34
  27. package/dist/excalidraw/actions/actionDuplicateSelection.js +105 -95
  28. package/dist/excalidraw/actions/actionFlip.js +16 -7
  29. package/dist/excalidraw/actions/actionFrame.d.ts +493 -0
  30. package/dist/excalidraw/actions/actionFrame.js +45 -2
  31. package/dist/excalidraw/actions/actionGroup.js +6 -4
  32. package/dist/excalidraw/actions/actionProperties.js +145 -116
  33. package/dist/excalidraw/actions/actionSelectAll.js +4 -3
  34. package/dist/excalidraw/actions/shortcuts.d.ts +1 -1
  35. package/dist/excalidraw/actions/shortcuts.js +1 -0
  36. package/dist/excalidraw/actions/types.d.ts +1 -1
  37. package/dist/excalidraw/align.d.ts +2 -1
  38. package/dist/excalidraw/align.js +15 -6
  39. package/dist/excalidraw/clipboard.d.ts +27 -5
  40. package/dist/excalidraw/clipboard.js +55 -28
  41. package/dist/excalidraw/components/Actions.d.ts +2 -1
  42. package/dist/excalidraw/components/Actions.js +4 -2
  43. package/dist/excalidraw/components/ActiveConfirmDialog.d.ts +1 -1
  44. package/dist/excalidraw/components/ActiveConfirmDialog.js +2 -3
  45. package/dist/excalidraw/components/App.d.ts +1 -0
  46. package/dist/excalidraw/components/App.js +216 -111
  47. package/dist/excalidraw/components/ColorPicker/ColorInput.js +2 -3
  48. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +2 -3
  49. package/dist/excalidraw/components/ColorPicker/CustomColorList.js +1 -1
  50. package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
  51. package/dist/excalidraw/components/ColorPicker/PickerColorList.js +1 -1
  52. package/dist/excalidraw/components/ColorPicker/ShadeList.js +1 -1
  53. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +1 -1
  54. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.js +1 -1
  55. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +3 -3
  56. package/dist/excalidraw/components/ConfirmDialog.js +17 -5
  57. package/dist/excalidraw/components/Dialog.js +2 -3
  58. package/dist/excalidraw/components/EyeDropper.d.ts +1 -1
  59. package/dist/excalidraw/components/EyeDropper.js +1 -1
  60. package/dist/excalidraw/components/IconPicker.d.ts +2 -2
  61. package/dist/excalidraw/components/IconPicker.js +56 -53
  62. package/dist/excalidraw/components/LayerUI.js +6 -6
  63. package/dist/excalidraw/components/LibraryMenu.d.ts +2 -16
  64. package/dist/excalidraw/components/LibraryMenu.js +70 -28
  65. package/dist/excalidraw/components/LibraryMenuHeaderContent.js +4 -5
  66. package/dist/excalidraw/components/MobileMenu.js +1 -1
  67. package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirm.js +2 -3
  68. package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
  69. package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.js +2 -3
  70. package/dist/excalidraw/components/Range.d.ts +9 -0
  71. package/dist/excalidraw/components/Range.js +24 -0
  72. package/dist/excalidraw/components/SearchMenu.d.ts +1 -1
  73. package/dist/excalidraw/components/SearchMenu.js +3 -4
  74. package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
  75. package/dist/excalidraw/components/Sidebar/Sidebar.js +2 -3
  76. package/dist/excalidraw/components/Stats/Collapsible.d.ts +2 -1
  77. package/dist/excalidraw/components/Stats/Collapsible.js +2 -2
  78. package/dist/excalidraw/components/Stats/Dimension.js +94 -8
  79. package/dist/excalidraw/components/Stats/MultiDimension.js +8 -5
  80. package/dist/excalidraw/components/Stats/Position.js +63 -3
  81. package/dist/excalidraw/components/Stats/index.js +21 -4
  82. package/dist/excalidraw/components/Stats/utils.d.ts +1 -1
  83. package/dist/excalidraw/components/Stats/utils.js +2 -55
  84. package/dist/excalidraw/components/TTDDialog/TTDDialog.js +1 -1
  85. package/dist/excalidraw/components/ToolButton.js +4 -9
  86. package/dist/excalidraw/components/hoc/withInternalFallback.js +3 -3
  87. package/dist/excalidraw/components/hyperlink/Hyperlink.js +6 -12
  88. package/dist/excalidraw/components/icons.d.ts +9 -0
  89. package/dist/excalidraw/components/icons.js +4 -4
  90. package/dist/excalidraw/components/main-menu/DefaultItems.js +2 -3
  91. package/dist/excalidraw/constants.d.ts +5 -1
  92. package/dist/excalidraw/constants.js +9 -1
  93. package/dist/excalidraw/context/tunnels.d.ts +2 -1
  94. package/dist/excalidraw/context/tunnels.js +3 -1
  95. package/dist/excalidraw/data/blob.d.ts +1 -0
  96. package/dist/excalidraw/data/blob.js +7 -3
  97. package/dist/excalidraw/data/filesystem.d.ts +2 -1
  98. package/dist/excalidraw/data/filesystem.js +1 -0
  99. package/dist/excalidraw/data/image.d.ts +0 -6
  100. package/dist/excalidraw/data/image.js +1 -43
  101. package/dist/excalidraw/data/index.js +6 -6
  102. package/dist/excalidraw/data/library.d.ts +9 -3
  103. package/dist/excalidraw/data/library.js +43 -6
  104. package/dist/excalidraw/data/restore.js +26 -8
  105. package/dist/excalidraw/data/url.d.ts +0 -1
  106. package/dist/excalidraw/data/url.js +2 -4
  107. package/dist/excalidraw/editor-jotai.d.ts +56 -0
  108. package/dist/excalidraw/editor-jotai.js +8 -0
  109. package/dist/excalidraw/element/binding.d.ts +9 -6
  110. package/dist/excalidraw/element/binding.js +124 -44
  111. package/dist/excalidraw/element/bounds.js +10 -0
  112. package/dist/excalidraw/element/cropElement.d.ts +5 -0
  113. package/dist/excalidraw/element/cropElement.js +28 -1
  114. package/dist/excalidraw/element/dragElements.js +13 -7
  115. package/dist/excalidraw/element/elbowArrow.d.ts +16 -0
  116. package/dist/excalidraw/element/elbowArrow.js +1268 -0
  117. package/dist/excalidraw/element/embeddable.js +4 -5
  118. package/dist/excalidraw/element/flowchart.d.ts +1 -1
  119. package/dist/excalidraw/element/flowchart.js +25 -9
  120. package/dist/excalidraw/element/heading.d.ts +5 -1
  121. package/dist/excalidraw/element/heading.js +5 -1
  122. package/dist/excalidraw/element/image.js +19 -5
  123. package/dist/excalidraw/element/linearElementEditor.d.ts +9 -10
  124. package/dist/excalidraw/element/linearElementEditor.js +97 -38
  125. package/dist/excalidraw/element/mutateElement.d.ts +3 -1
  126. package/dist/excalidraw/element/mutateElement.js +31 -4
  127. package/dist/excalidraw/element/newElement.d.ts +8 -12
  128. package/dist/excalidraw/element/newElement.js +36 -21
  129. package/dist/excalidraw/element/resizeElements.d.ts +20 -5
  130. package/dist/excalidraw/element/resizeElements.js +593 -361
  131. package/dist/excalidraw/element/sortElements.js +1 -4
  132. package/dist/excalidraw/element/types.d.ts +23 -1
  133. package/dist/excalidraw/fonts/Fonts.d.ts +0 -16
  134. package/dist/excalidraw/fonts/Fonts.js +6 -31
  135. package/dist/excalidraw/frame.d.ts +11 -5
  136. package/dist/excalidraw/frame.js +146 -35
  137. package/dist/excalidraw/groups.js +3 -0
  138. package/dist/excalidraw/hooks/useLibraryItemSvg.d.ts +1 -1
  139. package/dist/excalidraw/hooks/useLibraryItemSvg.js +2 -3
  140. package/dist/excalidraw/hooks/useScrollPosition.js +1 -1
  141. package/dist/excalidraw/i18n.js +3 -4
  142. package/dist/excalidraw/index.js +3 -4
  143. package/dist/excalidraw/locales/en.json +8 -1
  144. package/dist/excalidraw/renderer/interactiveScene.js +43 -32
  145. package/dist/excalidraw/renderer/staticScene.js +6 -4
  146. package/dist/excalidraw/renderer/staticSvgScene.js +1 -1
  147. package/dist/excalidraw/scene/Shape.js +40 -17
  148. package/dist/excalidraw/scene/comparisons.d.ts +0 -477
  149. package/dist/excalidraw/scene/comparisons.js +0 -37
  150. package/dist/excalidraw/scene/export.d.ts +7 -0
  151. package/dist/excalidraw/scene/export.js +107 -43
  152. package/dist/excalidraw/scene/index.d.ts +1 -1
  153. package/dist/excalidraw/scene/index.js +1 -1
  154. package/dist/excalidraw/scene/selection.js +4 -1
  155. package/dist/excalidraw/types.d.ts +15 -0
  156. package/dist/excalidraw/utility-types.d.ts +1 -0
  157. package/dist/excalidraw/utils.d.ts +8 -1
  158. package/dist/excalidraw/utils.js +9 -0
  159. package/dist/excalidraw/visualdebug.d.ts +8 -1
  160. package/dist/excalidraw/visualdebug.js +3 -0
  161. package/dist/math/line.d.ts +19 -0
  162. package/dist/math/line.js +32 -3
  163. package/dist/math/point.d.ts +10 -0
  164. package/dist/math/point.js +12 -1
  165. package/dist/prod/index.css +1 -1
  166. package/dist/prod/index.js +29 -44
  167. package/dist/{dev/locales/en-ZXYG7GCR.json → prod/locales/en-V6KXFSCK.json} +8 -1
  168. package/package.json +5 -2
  169. package/dist/browser/dev/excalidraw-assets-dev/chunk-JGDL4H2X.js.map +0 -7
  170. package/dist/browser/dev/excalidraw-assets-dev/chunk-V7NFEZA6.js.map +0 -7
  171. package/dist/browser/prod/excalidraw-assets/chunk-S2XKB3DE.js +0 -62
  172. package/dist/browser/prod/excalidraw-assets/image-OFI2YYMP.js +0 -1
  173. package/dist/excalidraw/element/routing.d.ts +0 -12
  174. package/dist/excalidraw/element/routing.js +0 -642
  175. package/dist/excalidraw/jotai.d.ts +0 -34
  176. package/dist/excalidraw/jotai.js +0 -18
  177. /package/dist/browser/dev/excalidraw-assets-dev/{en-ZSVWGT55.js.map → en-7IBTMWBG.js.map} +0 -0
  178. /package/dist/browser/dev/excalidraw-assets-dev/{image-RJG3J34Y.js.map → image-N5AC7SEK.js.map} +0 -0
@@ -5,18 +5,17 @@ import { duplicateElement, getNonDeletedElements } from "../element";
5
5
  import { isSomeElementSelected } from "../scene";
6
6
  import { ToolButton } from "../components/ToolButton";
7
7
  import { t } from "../i18n";
8
- import { arrayToMap, getShortcutKey } from "../utils";
8
+ import { arrayToMap, castArray, findLastIndex, getShortcutKey, invariant, } from "../utils";
9
9
  import { LinearElementEditor } from "../element/linearElementEditor";
10
10
  import { selectGroupsForSelectedElements, getSelectedGroupForElement, getElementsInGroup, } from "../groups";
11
11
  import { fixBindingsAfterDuplication } from "../element/binding";
12
12
  import { DEFAULT_GRID_SIZE } from "../constants";
13
- import { bindTextToShapeAfterDuplication, getBoundTextElement, } from "../element/textElement";
14
- import { isBoundToContainer, isFrameLikeElement } from "../element/typeChecks";
13
+ import { bindTextToShapeAfterDuplication, getBoundTextElement, getContainerElement, } from "../element/textElement";
14
+ import { hasBoundTextElement, isBoundToContainer, isFrameLikeElement, } from "../element/typeChecks";
15
15
  import { normalizeElementOrder } from "../element/sortElements";
16
16
  import { DuplicateIcon } from "../components/icons";
17
17
  import { bindElementsToFramesAfterDuplication, getFrameChildren, } from "../frame";
18
18
  import { excludeElementsInFramesFromSelection, getSelectedElements, } from "../scene/selection";
19
- import { syncMovedIndices } from "../fractionalIndex";
20
19
  import { StoreAction } from "../store";
21
20
  export const actionDuplicateSelection = register({
22
21
  name: "duplicateSelection",
@@ -39,8 +38,15 @@ export const actionDuplicateSelection = register({
39
38
  return false;
40
39
  }
41
40
  }
41
+ const nextState = duplicateElements(elements, appState);
42
+ if (app.props.onDuplicate && nextState.elements) {
43
+ const mappedElements = app.props.onDuplicate(nextState.elements, elements);
44
+ if (mappedElements) {
45
+ nextState.elements = mappedElements;
46
+ }
47
+ }
42
48
  return {
43
- ...duplicateElements(elements, appState),
49
+ ...nextState,
44
50
  storeAction: StoreAction.CAPTURE,
45
51
  };
46
52
  },
@@ -49,25 +55,35 @@ export const actionDuplicateSelection = register({
49
55
  });
50
56
  const duplicateElements = (elements, appState) => {
51
57
  // ---------------------------------------------------------------------------
52
- // step (1)
53
- const sortedElements = normalizeElementOrder(elements);
54
58
  const groupIdMap = new Map();
55
59
  const newElements = [];
56
60
  const oldElements = [];
57
61
  const oldIdToDuplicatedId = new Map();
58
62
  const duplicatedElementsMap = new Map();
63
+ const elementsMap = arrayToMap(elements);
59
64
  const duplicateAndOffsetElement = (element) => {
60
- const newElement = duplicateElement(appState.editingGroupId, groupIdMap, element, {
61
- x: element.x + DEFAULT_GRID_SIZE / 2,
62
- y: element.y + DEFAULT_GRID_SIZE / 2,
63
- });
64
- duplicatedElementsMap.set(newElement.id, newElement);
65
- oldIdToDuplicatedId.set(element.id, newElement.id);
66
- oldElements.push(element);
67
- newElements.push(newElement);
68
- return newElement;
65
+ const elements = castArray(element);
66
+ const _newElements = elements.reduce((acc, element) => {
67
+ if (processedIds.has(element.id)) {
68
+ return acc;
69
+ }
70
+ processedIds.set(element.id, true);
71
+ const newElement = duplicateElement(appState.editingGroupId, groupIdMap, element, {
72
+ x: element.x + DEFAULT_GRID_SIZE / 2,
73
+ y: element.y + DEFAULT_GRID_SIZE / 2,
74
+ });
75
+ processedIds.set(newElement.id, true);
76
+ duplicatedElementsMap.set(newElement.id, newElement);
77
+ oldIdToDuplicatedId.set(element.id, newElement.id);
78
+ oldElements.push(element);
79
+ newElements.push(newElement);
80
+ acc.push(newElement);
81
+ return acc;
82
+ }, []);
83
+ return (Array.isArray(element) ? _newElements : _newElements[0] || null);
69
84
  };
70
- const idsOfElementsToDuplicate = arrayToMap(getSelectedElements(sortedElements, appState, {
85
+ elements = normalizeElementOrder(elements);
86
+ const idsOfElementsToDuplicate = arrayToMap(getSelectedElements(elements, appState, {
71
87
  includeBoundTextElement: true,
72
88
  includeElementsInFrames: true,
73
89
  }));
@@ -82,97 +98,91 @@ const duplicateElements = (elements, appState) => {
82
98
  // For convenience we mark even the newly created ones even though we don't
83
99
  // loop over them.
84
100
  const processedIds = new Map();
85
- const markAsProcessed = (elements) => {
86
- for (const element of elements) {
87
- processedIds.set(element.id, true);
101
+ const elementsWithClones = elements.slice();
102
+ const insertAfterIndex = (index, elements) => {
103
+ invariant(index !== -1, "targetIndex === -1 ");
104
+ if (!Array.isArray(elements) && !elements) {
105
+ return;
88
106
  }
89
- return elements;
107
+ elementsWithClones.splice(index + 1, 0, ...castArray(elements));
90
108
  };
91
- const elementsWithClones = [];
92
- let index = -1;
93
- while (++index < sortedElements.length) {
94
- const element = sortedElements[index];
95
- if (processedIds.get(element.id)) {
109
+ const frameIdsToDuplicate = new Set(elements
110
+ .filter((el) => idsOfElementsToDuplicate.has(el.id) && isFrameLikeElement(el))
111
+ .map((el) => el.id));
112
+ for (const element of elements) {
113
+ if (processedIds.has(element.id)) {
96
114
  continue;
97
115
  }
98
- const boundTextElement = getBoundTextElement(element, arrayToMap(elements));
99
- const isElementAFrameLike = isFrameLikeElement(element);
100
- if (idsOfElementsToDuplicate.get(element.id)) {
101
- // if a group or a container/bound-text or frame, duplicate atomically
102
- if (element.groupIds.length || boundTextElement || isElementAFrameLike) {
103
- const groupId = getSelectedGroupForElement(appState, element);
104
- if (groupId) {
105
- // TODO:
106
- // remove `.flatMap...`
107
- // if the elements in a frame are grouped when the frame is grouped
108
- const groupElements = getElementsInGroup(sortedElements, groupId).flatMap((element) => isFrameLikeElement(element)
109
- ? [...getFrameChildren(elements, element.id), element]
110
- : [element]);
111
- elementsWithClones.push(...markAsProcessed([
112
- ...groupElements,
113
- ...groupElements.map((element) => duplicateAndOffsetElement(element)),
114
- ]));
115
- continue;
116
- }
117
- if (boundTextElement) {
118
- elementsWithClones.push(...markAsProcessed([
119
- element,
120
- boundTextElement,
121
- duplicateAndOffsetElement(element),
122
- duplicateAndOffsetElement(boundTextElement),
123
- ]));
124
- continue;
125
- }
126
- if (isElementAFrameLike) {
127
- const elementsInFrame = getFrameChildren(sortedElements, element.id);
128
- elementsWithClones.push(...markAsProcessed([
129
- ...elementsInFrame,
130
- element,
131
- ...elementsInFrame.map((e) => duplicateAndOffsetElement(e)),
132
- duplicateAndOffsetElement(element),
133
- ]));
134
- continue;
135
- }
116
+ if (!idsOfElementsToDuplicate.has(element.id)) {
117
+ continue;
118
+ }
119
+ // groups
120
+ // -------------------------------------------------------------------------
121
+ const groupId = getSelectedGroupForElement(appState, element);
122
+ if (groupId) {
123
+ const groupElements = getElementsInGroup(elements, groupId).flatMap((element) => isFrameLikeElement(element)
124
+ ? [...getFrameChildren(elements, element.id), element]
125
+ : [element]);
126
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
127
+ return el.groupIds?.includes(groupId);
128
+ });
129
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement(groupElements));
130
+ continue;
131
+ }
132
+ // frame duplication
133
+ // -------------------------------------------------------------------------
134
+ if (element.frameId && frameIdsToDuplicate.has(element.frameId)) {
135
+ continue;
136
+ }
137
+ if (isFrameLikeElement(element)) {
138
+ const frameId = element.id;
139
+ const frameChildren = getFrameChildren(elements, frameId);
140
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
141
+ return el.frameId === frameId || el.id === frameId;
142
+ });
143
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement([...frameChildren, element]));
144
+ continue;
145
+ }
146
+ // text container
147
+ // -------------------------------------------------------------------------
148
+ if (hasBoundTextElement(element)) {
149
+ const boundTextElement = getBoundTextElement(element, elementsMap);
150
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
151
+ return (el.id === element.id ||
152
+ ("containerId" in el && el.containerId === element.id));
153
+ });
154
+ if (boundTextElement) {
155
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement([element, boundTextElement]));
136
156
  }
137
- // since elements in frames have a lower z-index than the frame itself,
138
- // they will be looped first and if their frames are selected as well,
139
- // they will have been copied along with the frame atomically in the
140
- // above branch, so we must skip those elements here
141
- //
142
- // now, for elements do not belong any frames or elements whose frames
143
- // are selected (or elements that are left out from the above
144
- // steps for whatever reason) we (should at least) duplicate them here
145
- if (!element.frameId || !idsOfElementsToDuplicate.has(element.frameId)) {
146
- elementsWithClones.push(...markAsProcessed([element, duplicateAndOffsetElement(element)]));
157
+ else {
158
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement(element));
147
159
  }
160
+ continue;
148
161
  }
149
- else {
150
- elementsWithClones.push(...markAsProcessed([element]));
151
- }
152
- }
153
- // step (2)
154
- // second pass to remove duplicates. We loop from the end as it's likelier
155
- // that the last elements are in the correct order (contiguous or otherwise).
156
- // Thus we need to reverse as the last step (3).
157
- const finalElementsReversed = [];
158
- const finalElementIds = new Map();
159
- index = elementsWithClones.length;
160
- while (--index >= 0) {
161
- const element = elementsWithClones[index];
162
- if (!finalElementIds.get(element.id)) {
163
- finalElementIds.set(element.id, true);
164
- finalElementsReversed.push(element);
162
+ if (isBoundToContainer(element)) {
163
+ const container = getContainerElement(element, elementsMap);
164
+ const targetIndex = findLastIndex(elementsWithClones, (el) => {
165
+ return el.id === element.id || el.id === container?.id;
166
+ });
167
+ if (container) {
168
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement([container, element]));
169
+ }
170
+ else {
171
+ insertAfterIndex(targetIndex, duplicateAndOffsetElement(element));
172
+ }
173
+ continue;
165
174
  }
175
+ // default duplication (regular elements)
176
+ // -------------------------------------------------------------------------
177
+ insertAfterIndex(findLastIndex(elementsWithClones, (el) => el.id === element.id), duplicateAndOffsetElement(element));
166
178
  }
167
- // step (3)
168
- const finalElements = syncMovedIndices(finalElementsReversed.reverse(), arrayToMap(newElements));
169
179
  // ---------------------------------------------------------------------------
170
180
  bindTextToShapeAfterDuplication(elementsWithClones, oldElements, oldIdToDuplicatedId);
171
181
  fixBindingsAfterDuplication(elementsWithClones, oldElements, oldIdToDuplicatedId);
172
- bindElementsToFramesAfterDuplication(finalElements, oldElements, oldIdToDuplicatedId);
182
+ bindElementsToFramesAfterDuplication(elementsWithClones, oldElements, oldIdToDuplicatedId);
173
183
  const nextElementsToSelect = excludeElementsInFramesFromSelection(newElements);
174
184
  return {
175
- elements: finalElements,
185
+ elements: elementsWithClones,
176
186
  appState: {
177
187
  ...appState,
178
188
  ...selectGroupsForSelectedElements({
@@ -183,7 +193,7 @@ const duplicateElements = (elements, appState) => {
183
193
  }
184
194
  return acc;
185
195
  }, {}),
186
- }, getNonDeletedElements(finalElements), appState, null),
196
+ }, getNonDeletedElements(elementsWithClones), appState, null),
187
197
  },
188
198
  };
189
199
  };
@@ -4,14 +4,14 @@ import { getNonDeletedElements } from "../element";
4
4
  import { resizeMultipleElements } from "../element/resizeElements";
5
5
  import { arrayToMap } from "../utils";
6
6
  import { CODES, KEYS } from "../keys";
7
- import { getCommonBoundingBox } from "../element/bounds";
8
7
  import { bindOrUnbindLinearElements, isBindingEnabled, } from "../element/binding";
9
8
  import { updateFrameMembershipOfSelectedElements } from "../frame";
10
9
  import { flipHorizontal, flipVertical } from "../components/icons";
11
10
  import { StoreAction } from "../store";
12
11
  import { isArrowElement, isElbowArrow, isLinearElement, } from "../element/typeChecks";
13
- import { mutateElbowArrow } from "../element/routing";
14
12
  import { mutateElement, newElementWith } from "../element/mutateElement";
13
+ import { deepCopyElement } from "../element/newElement";
14
+ import { getCommonBoundingBox } from "../element/bounds";
15
15
  export const actionFlipHorizontal = register({
16
16
  name: "flipHorizontal",
17
17
  label: "labels.flipHorizontal",
@@ -59,9 +59,17 @@ const flipElements = (selectedElements, elementsMap, appState, flipDirection, ap
59
59
  });
60
60
  });
61
61
  }
62
- const { minX, minY, maxX, maxY, midX, midY } = getCommonBoundingBox(selectedElements);
63
- resizeMultipleElements(elementsMap, selectedElements, elementsMap, "nw", true, true, flipDirection === "horizontal" ? maxX : minX, flipDirection === "horizontal" ? minY : maxY);
64
- bindOrUnbindLinearElements(selectedElements.filter(isLinearElement), elementsMap, app.scene.getNonDeletedElements(), app.scene, isBindingEnabled(appState), []);
62
+ const { midX, midY } = getCommonBoundingBox(selectedElements);
63
+ resizeMultipleElements(selectedElements, elementsMap, "nw", app.scene, new Map(Array.from(elementsMap.values()).map((element) => [
64
+ element.id,
65
+ deepCopyElement(element),
66
+ ])), {
67
+ flipByX: flipDirection === "horizontal",
68
+ flipByY: flipDirection === "vertical",
69
+ shouldResizeFromCenter: true,
70
+ shouldMaintainAspectRatio: true,
71
+ });
72
+ bindOrUnbindLinearElements(selectedElements.filter(isLinearElement), elementsMap, app.scene.getNonDeletedElements(), app.scene, isBindingEnabled(appState), [], appState.zoom);
65
73
  // ---------------------------------------------------------------------------
66
74
  // flipping arrow elements (and potentially other) makes the selection group
67
75
  // "move" across the canvas because of how arrows can bump against the "wall"
@@ -76,8 +84,9 @@ const flipElements = (selectedElements, elementsMap, appState, flipDirection, ap
76
84
  x: element.x + diffX,
77
85
  y: element.y + diffY,
78
86
  }));
79
- elbowArrows.forEach((element) => mutateElbowArrow(element, elementsMap, element.points, undefined, undefined, {
80
- informMutation: false,
87
+ elbowArrows.forEach((element) => mutateElement(element, {
88
+ x: element.x + diffX,
89
+ y: element.y + diffY,
81
90
  }));
82
91
  // ---------------------------------------------------------------------------
83
92
  return selectedElements;