@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
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState } from "react";
3
3
  import { t } from "../i18n";
4
4
  import Trans from "./Trans";
5
- import { jotaiScope } from "../jotai";
5
+ import { useAtom } from "../editor-jotai";
6
6
  import { useApp, useExcalidrawSetAppState } from "./App";
7
7
  import { saveLibraryAsJSON } from "../data/json";
8
8
  import { libraryItemsAtom } from "../data/library";
@@ -10,7 +10,6 @@ import { DotsIcon, ExportIcon, LoadIcon, publishIcon, TrashIcon, } from "./icons
10
10
  import { ToolButton } from "./ToolButton";
11
11
  import { fileOpen } from "../data/filesystem";
12
12
  import { muteFSAbortError } from "../utils";
13
- import { useAtom } from "jotai";
14
13
  import ConfirmDialog from "./ConfirmDialog";
15
14
  import PublishLibrary from "./PublishLibrary";
16
15
  import { Dialog } from "./Dialog";
@@ -21,8 +20,8 @@ import clsx from "clsx";
21
20
  import { useLibraryCache } from "../hooks/useLibraryItemSvg";
22
21
  const getSelectedItems = (libraryItems, selectedItems) => libraryItems.filter((item) => selectedItems.includes(item.id));
23
22
  export const LibraryDropdownMenuButton = ({ setAppState, selectedItems, library, onRemoveFromLibrary, resetLibrary, onSelectItems, appState, className, }) => {
24
- const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
25
- const [isLibraryMenuOpen, setIsLibraryMenuOpen] = useAtom(isLibraryMenuOpenAtom, jotaiScope);
23
+ const [libraryItemsData] = useAtom(libraryItemsAtom);
24
+ const [isLibraryMenuOpen, setIsLibraryMenuOpen] = useAtom(isLibraryMenuOpenAtom);
26
25
  const renderRemoveLibAlert = () => {
27
26
  const content = selectedItems.length
28
27
  ? t("alerts.removeItemsFromsLibrary", { count: selectedItems.length })
@@ -109,7 +108,7 @@ export const LibraryDropdownMenu = ({ selectedItems, onSelectItems, className, }
109
108
  const { clearLibraryCache, deleteItemsFromLibraryCache } = useLibraryCache();
110
109
  const appState = useUIAppState();
111
110
  const setAppState = useExcalidrawSetAppState();
112
- const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
111
+ const [libraryItemsData] = useAtom(libraryItemsAtom);
113
112
  const removeFromLibrary = async (libraryItems) => {
114
113
  const nextItems = libraryItems.filter((item) => !selectedItems.includes(item.id));
115
114
  library.setLibrary(nextItems).catch(() => {
@@ -36,7 +36,7 @@ export const MobileMenu = ({ appState, elements, actionManager, setAppState, onL
36
36
  }, children: _jsxs(Island, { padding: 0, children: [appState.openMenu === "shape" &&
37
37
  !appState.viewModeEnabled &&
38
38
  appState.openDialog?.name !== "elementLinkSelector" &&
39
- showSelectedShapeActions(appState, elements) ? (_jsx(Section, { className: "App-mobile-menu", heading: "selectedShapeActions", children: _jsx(SelectedShapeActions, { appState: appState, elementsMap: app.scene.getNonDeletedElementsMap(), renderAction: actionManager.renderAction }) })) : null, _jsxs("footer", { className: "App-toolbar", children: [renderAppToolbar(), appState.scrolledOutside &&
39
+ showSelectedShapeActions(appState, elements) ? (_jsx(Section, { className: "App-mobile-menu", heading: "selectedShapeActions", children: _jsx(SelectedShapeActions, { appState: appState, elementsMap: app.scene.getNonDeletedElementsMap(), renderAction: actionManager.renderAction, app: app }) })) : null, _jsxs("footer", { className: "App-toolbar", children: [renderAppToolbar(), appState.scrolledOutside &&
40
40
  !appState.openMenu &&
41
41
  !appState.openSidebar && (_jsx("button", { type: "button", className: "scroll-back-to-content", onClick: () => {
42
42
  setAppState((appState) => ({
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useAtom } from "jotai";
3
2
  import { useTunnels } from "../../context/tunnels";
4
- import { jotaiScope } from "../../jotai";
3
+ import { useAtom } from "../../editor-jotai";
5
4
  import { Dialog } from "../Dialog";
6
5
  import { withInternalFallback } from "../hoc/withInternalFallback";
7
6
  import { overwriteConfirmStateAtom } from "./OverwriteConfirmState";
@@ -11,7 +10,7 @@ import { Actions, Action } from "./OverwriteConfirmActions";
11
10
  import "./OverwriteConfirm.scss";
12
11
  const OverwriteConfirmDialog = Object.assign(withInternalFallback("OverwriteConfirmDialog", ({ children }) => {
13
12
  const { OverwriteConfirmDialogTunnel } = useTunnels();
14
- const [overwriteConfirmState, setState] = useAtom(overwriteConfirmStateAtom, jotaiScope);
13
+ const [overwriteConfirmState, setState] = useAtom(overwriteConfirmStateAtom);
15
14
  if (!overwriteConfirmState.active) {
16
15
  return null;
17
16
  }
@@ -11,7 +11,7 @@ export type OverwriteConfirmState = {
11
11
  } | {
12
12
  active: false;
13
13
  };
14
- export declare const overwriteConfirmStateAtom: import("jotai").PrimitiveAtom<OverwriteConfirmState> & {
14
+ export declare const overwriteConfirmStateAtom: import("jotai/vanilla/atom").PrimitiveAtom<OverwriteConfirmState> & {
15
15
  init: OverwriteConfirmState;
16
16
  };
17
17
  export declare function openConfirmModal({ title, description, actionLabel, color, }: {
@@ -1,11 +1,10 @@
1
- import { atom } from "jotai";
2
- import { jotaiStore } from "../../jotai";
1
+ import { atom, editorJotaiStore } from "../../editor-jotai";
3
2
  export const overwriteConfirmStateAtom = atom({
4
3
  active: false,
5
4
  });
6
5
  export async function openConfirmModal({ title, description, actionLabel, color, }) {
7
6
  return new Promise((resolve) => {
8
- jotaiStore.set(overwriteConfirmStateAtom, {
7
+ editorJotaiStore.set(overwriteConfirmStateAtom, {
9
8
  active: true,
10
9
  onConfirm: () => resolve(true),
11
10
  onClose: () => resolve(false),
@@ -0,0 +1,9 @@
1
+ /// <reference types="react" />
2
+ import "./Range.scss";
3
+ export type RangeProps = {
4
+ updateData: (value: number) => void;
5
+ appState: any;
6
+ elements: any;
7
+ testId?: string;
8
+ };
9
+ export declare const Range: ({ updateData, appState, elements, testId, }: RangeProps) => JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect } from "react";
3
+ import { getFormValue } from "../actions/actionProperties";
4
+ import { t } from "../i18n";
5
+ import "./Range.scss";
6
+ export const Range = ({ updateData, appState, elements, testId, }) => {
7
+ const rangeRef = React.useRef(null);
8
+ const valueRef = React.useRef(null);
9
+ const value = getFormValue(elements, appState, (element) => element.opacity, true, appState.currentItemOpacity);
10
+ useEffect(() => {
11
+ if (rangeRef.current && valueRef.current) {
12
+ const rangeElement = rangeRef.current;
13
+ const valueElement = valueRef.current;
14
+ const inputWidth = rangeElement.offsetWidth;
15
+ const thumbWidth = 15; // 15 is the width of the thumb
16
+ const position = (value / 100) * (inputWidth - thumbWidth) + thumbWidth / 2;
17
+ valueElement.style.left = `${position}px`;
18
+ rangeElement.style.background = `linear-gradient(to right, var(--color-slider-track) 0%, var(--color-slider-track) ${value}%, var(--button-bg) ${value}%, var(--button-bg) 100%)`;
19
+ }
20
+ }, [value]);
21
+ return (_jsxs("label", { className: "control-label", children: [t("labels.opacity"), _jsxs("div", { className: "range-wrapper", children: [_jsx("input", { ref: rangeRef, type: "range", min: "0", max: "100", step: "10", onChange: (event) => {
22
+ updateData(+event.target.value);
23
+ }, value: value, className: "range-input", "data-testid": testId }), _jsx("div", { className: "value-bubble", ref: valueRef, children: value !== 0 ? value : null }), _jsx("div", { className: "zero-label", children: "0" })] })] }));
24
+ };
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import "./SearchMenu.scss";
3
- export declare const searchItemInFocusAtom: import("jotai").PrimitiveAtom<number | null> & {
3
+ export declare const searchItemInFocusAtom: import("jotai/vanilla/atom").PrimitiveAtom<number | null> & {
4
4
  init: number | null;
5
5
  };
6
6
  export declare const SearchMenu: () => JSX.Element;
@@ -10,8 +10,7 @@ import { measureText } from "../element/textElement";
10
10
  import { addEventListener, getFontString } from "../utils";
11
11
  import { KEYS } from "../keys";
12
12
  import clsx from "clsx";
13
- import { atom, useAtom } from "jotai";
14
- import { jotaiScope } from "../jotai";
13
+ import { atom, useAtom } from "../editor-jotai";
15
14
  import { t } from "../i18n";
16
15
  import { isElementCompletelyInViewport } from "../element/sizeHelpers";
17
16
  import { randomInteger } from "../random";
@@ -26,7 +25,7 @@ export const SearchMenu = () => {
26
25
  const app = useApp();
27
26
  const setAppState = useExcalidrawSetAppState();
28
27
  const searchInputRef = useRef(null);
29
- const [inputValue, setInputValue] = useAtom(searchQueryAtom, jotaiScope);
28
+ const [inputValue, setInputValue] = useAtom(searchQueryAtom);
30
29
  const searchQuery = inputValue.trim();
31
30
  const [isSearching, setIsSearching] = useState(false);
32
31
  const [searchMatches, setSearchMatches] = useState({
@@ -35,7 +34,7 @@ export const SearchMenu = () => {
35
34
  });
36
35
  const searchedQueryRef = useRef(null);
37
36
  const lastSceneNonceRef = useRef(undefined);
38
- const [focusIndex, setFocusIndex] = useAtom(searchItemInFocusAtom, jotaiScope);
37
+ const [focusIndex, setFocusIndex] = useAtom(searchItemInFocusAtom);
39
38
  const elementsMap = app.scene.getNonDeletedElementsMap();
40
39
  useEffect(() => {
41
40
  if (isSearching) {
@@ -8,7 +8,7 @@ import "./Sidebar.scss";
8
8
  *
9
9
  * Since we can only render one Sidebar at a time, we can use a simple flag.
10
10
  */
11
- export declare const isSidebarDockedAtom: import("jotai").PrimitiveAtom<boolean> & {
11
+ export declare const isSidebarDockedAtom: import("jotai/vanilla/atom").PrimitiveAtom<boolean> & {
12
12
  init: boolean;
13
13
  };
14
14
  export declare const SidebarInner: React.ForwardRefExoticComponent<Omit<{
@@ -2,8 +2,7 @@ import { createElement as _createElement } from "react";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useLayoutEffect, useRef, useState, forwardRef, useImperativeHandle, useCallback, } from "react";
4
4
  import { Island } from "../Island";
5
- import { atom, useSetAtom } from "jotai";
6
- import { jotaiScope } from "../../jotai";
5
+ import { atom, useSetAtom } from "../../editor-jotai";
7
6
  import { SidebarPropsContext } from "./common";
8
7
  import { SidebarHeader } from "./SidebarHeader";
9
8
  import clsx from "clsx";
@@ -33,7 +32,7 @@ export const SidebarInner = forwardRef(({ name, children, onDock, docked, classN
33
32
  console.warn("Sidebar: `docked` must be set when `onDock` is supplied for the sidebar to be user-dockable. To hide this message, either pass `docked` or remove `onDock`");
34
33
  }
35
34
  const setAppState = useExcalidrawSetAppState();
36
- const setIsSidebarDockedAtom = useSetAtom(isSidebarDockedAtom, jotaiScope);
35
+ const setIsSidebarDockedAtom = useSetAtom(isSidebarDockedAtom);
37
36
  useLayoutEffect(() => {
38
37
  setIsSidebarDockedAtom(!!docked);
39
38
  return () => {
@@ -4,6 +4,7 @@ interface CollapsibleProps {
4
4
  open: boolean;
5
5
  openTrigger: () => void;
6
6
  children: React.ReactNode;
7
+ className?: string;
7
8
  }
8
- declare const Collapsible: ({ label, open, openTrigger, children, }: CollapsibleProps) => JSX.Element;
9
+ declare const Collapsible: ({ label, open, openTrigger, children, className, }: CollapsibleProps) => JSX.Element;
9
10
  export default Collapsible;
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { InlineIcon } from "../InlineIcon";
3
3
  import { collapseDownIcon, collapseUpIcon } from "../icons";
4
- const Collapsible = ({ label, open, openTrigger, children, }) => {
4
+ const Collapsible = ({ label, open, openTrigger, children, className, }) => {
5
5
  return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
6
6
  cursor: "pointer",
7
7
  display: "flex",
8
8
  justifyContent: "space-between",
9
9
  alignItems: "center",
10
- }, onClick: openTrigger, children: [label, _jsx(InlineIcon, { icon: open ? collapseUpIcon : collapseDownIcon })] }), open && (_jsx("div", { style: { display: "flex", flexDirection: "column" }, children: children }))] }));
10
+ }, className: className, onClick: openTrigger, children: [label, _jsx(InlineIcon, { icon: open ? collapseUpIcon : collapseDownIcon })] }), open && (_jsx("div", { style: { display: "flex", flexDirection: "column" }, children: children }))] }));
11
11
  };
12
12
  export default Collapsible;
@@ -1,18 +1,87 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import DragInput from "./DragInput";
3
- import { getStepSizedValue, isPropertyEditable, resizeElement } from "./utils";
3
+ import { getStepSizedValue, isPropertyEditable } from "./utils";
4
4
  import { MIN_WIDTH_OR_HEIGHT } from "../../constants";
5
+ import { resizeSingleElement } from "../../element/resizeElements";
6
+ import { isImageElement } from "../../element/typeChecks";
7
+ import { MINIMAL_CROP_SIZE, getUncroppedWidthAndHeight, } from "../../element/cropElement";
8
+ import { mutateElement } from "../../element/mutateElement";
9
+ import { clamp, round } from "../../../math";
5
10
  const STEP_SIZE = 10;
6
11
  const _shouldKeepAspectRatio = (element) => {
7
12
  return element.type === "image";
8
13
  };
9
- const handleDimensionChange = ({ accumulatedChange, originalElements, shouldKeepAspectRatio, shouldChangeByStepSize, nextValue, property, scene, }) => {
14
+ const handleDimensionChange = ({ accumulatedChange, originalElements, originalElementsMap, shouldKeepAspectRatio, shouldChangeByStepSize, nextValue, property, originalAppState, instantChange, scene, }) => {
10
15
  const elementsMap = scene.getNonDeletedElementsMap();
11
- const elements = scene.getNonDeletedElements();
12
16
  const origElement = originalElements[0];
13
- if (origElement) {
17
+ const latestElement = elementsMap.get(origElement.id);
18
+ if (origElement && latestElement) {
14
19
  const keepAspectRatio = shouldKeepAspectRatio || _shouldKeepAspectRatio(origElement);
15
20
  const aspectRatio = origElement.width / origElement.height;
21
+ if (originalAppState.croppingElementId === origElement.id) {
22
+ const element = elementsMap.get(origElement.id);
23
+ if (!element || !isImageElement(element) || !element.crop) {
24
+ return;
25
+ }
26
+ const crop = element.crop;
27
+ let nextCrop = { ...crop };
28
+ const isFlippedByX = element.scale[0] === -1;
29
+ const isFlippedByY = element.scale[1] === -1;
30
+ const { width: uncroppedWidth, height: uncroppedHeight } = getUncroppedWidthAndHeight(element);
31
+ const naturalToUncroppedWidthRatio = crop.naturalWidth / uncroppedWidth;
32
+ const naturalToUncroppedHeightRatio = crop.naturalHeight / uncroppedHeight;
33
+ const MAX_POSSIBLE_WIDTH = isFlippedByX
34
+ ? crop.width + crop.x
35
+ : crop.naturalWidth - crop.x;
36
+ const MAX_POSSIBLE_HEIGHT = isFlippedByY
37
+ ? crop.height + crop.y
38
+ : crop.naturalHeight - crop.y;
39
+ const MIN_WIDTH = MINIMAL_CROP_SIZE * naturalToUncroppedWidthRatio;
40
+ const MIN_HEIGHT = MINIMAL_CROP_SIZE * naturalToUncroppedHeightRatio;
41
+ if (nextValue !== undefined) {
42
+ if (property === "width") {
43
+ const nextValueInNatural = nextValue * naturalToUncroppedWidthRatio;
44
+ const nextCropWidth = clamp(nextValueInNatural, MIN_WIDTH, MAX_POSSIBLE_WIDTH);
45
+ nextCrop = {
46
+ ...nextCrop,
47
+ width: nextCropWidth,
48
+ x: isFlippedByX ? crop.x + crop.width - nextCropWidth : crop.x,
49
+ };
50
+ }
51
+ else if (property === "height") {
52
+ const nextValueInNatural = nextValue * naturalToUncroppedHeightRatio;
53
+ const nextCropHeight = clamp(nextValueInNatural, MIN_HEIGHT, MAX_POSSIBLE_HEIGHT);
54
+ nextCrop = {
55
+ ...nextCrop,
56
+ height: nextCropHeight,
57
+ y: isFlippedByY ? crop.y + crop.height - nextCropHeight : crop.y,
58
+ };
59
+ }
60
+ mutateElement(element, {
61
+ crop: nextCrop,
62
+ width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
63
+ height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
64
+ });
65
+ return;
66
+ }
67
+ const changeInWidth = property === "width" ? instantChange : 0;
68
+ const changeInHeight = property === "height" ? instantChange : 0;
69
+ const nextCropWidth = clamp(crop.width + changeInWidth, MIN_WIDTH, MAX_POSSIBLE_WIDTH);
70
+ const nextCropHeight = clamp(crop.height + changeInHeight, MIN_WIDTH, MAX_POSSIBLE_HEIGHT);
71
+ nextCrop = {
72
+ ...crop,
73
+ x: isFlippedByX ? crop.x + crop.width - nextCropWidth : crop.x,
74
+ y: isFlippedByY ? crop.y + crop.height - nextCropHeight : crop.y,
75
+ width: nextCropWidth,
76
+ height: nextCropHeight,
77
+ };
78
+ mutateElement(element, {
79
+ crop: nextCrop,
80
+ width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
81
+ height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
82
+ });
83
+ return;
84
+ }
16
85
  if (nextValue !== undefined) {
17
86
  const nextWidth = Math.max(property === "width"
18
87
  ? nextValue
@@ -24,7 +93,9 @@ const handleDimensionChange = ({ accumulatedChange, originalElements, shouldKeep
24
93
  : keepAspectRatio
25
94
  ? nextValue / aspectRatio
26
95
  : origElement.height, MIN_WIDTH_OR_HEIGHT);
27
- resizeElement(nextWidth, nextHeight, keepAspectRatio, origElement, elementsMap, elements, scene);
96
+ resizeSingleElement(nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElementsMap, property === "width" ? "e" : "s", {
97
+ shouldMaintainAspectRatio: keepAspectRatio,
98
+ });
28
99
  return;
29
100
  }
30
101
  const changeInWidth = property === "width" ? accumulatedChange : 0;
@@ -57,12 +128,27 @@ const handleDimensionChange = ({ accumulatedChange, originalElements, shouldKeep
57
128
  }
58
129
  nextHeight = Math.max(MIN_WIDTH_OR_HEIGHT, nextHeight);
59
130
  nextWidth = Math.max(MIN_WIDTH_OR_HEIGHT, nextWidth);
60
- resizeElement(nextWidth, nextHeight, keepAspectRatio, origElement, elementsMap, elements, scene);
131
+ resizeSingleElement(nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElementsMap, property === "width" ? "e" : "s", {
132
+ shouldMaintainAspectRatio: keepAspectRatio,
133
+ });
61
134
  }
62
135
  };
63
136
  const DimensionDragInput = ({ property, element, scene, appState, }) => {
64
- const value = Math.round((property === "width" ? element.width : element.height) * 100) /
65
- 100;
137
+ let value = round(property === "width" ? element.width : element.height, 2);
138
+ if (appState.croppingElementId &&
139
+ appState.croppingElementId === element.id &&
140
+ isImageElement(element) &&
141
+ element.crop) {
142
+ const { width: uncroppedWidth, height: uncroppedHeight } = getUncroppedWidthAndHeight(element);
143
+ if (property === "width") {
144
+ const ratio = uncroppedWidth / element.crop.naturalWidth;
145
+ value = round(element.crop.width * ratio, 2);
146
+ }
147
+ if (property === "height") {
148
+ const ratio = uncroppedHeight / element.crop.naturalHeight;
149
+ value = round(element.crop.height * ratio, 2);
150
+ }
151
+ }
66
152
  return (_jsx(DragInput, { label: property === "width" ? "W" : "H", elements: [element], dragInputCallback: handleDimensionChange, value: value, editable: isPropertyEditable(element, property), scene: scene, appState: appState, property: property }));
67
153
  };
68
154
  export default DimensionDragInput;
@@ -3,11 +3,11 @@ import { useMemo } from "react";
3
3
  import { getCommonBounds, isTextElement } from "../../element";
4
4
  import { updateBoundElements } from "../../element/binding";
5
5
  import { mutateElement } from "../../element/mutateElement";
6
- import { rescalePointsInElement } from "../../element/resizeElements";
6
+ import { rescalePointsInElement, resizeSingleElement, } from "../../element/resizeElements";
7
7
  import { getBoundTextElement, handleBindTextResize, } from "../../element/textElement";
8
8
  import DragInput from "./DragInput";
9
9
  import { getAtomicUnits, getStepSizedValue, isPropertyEditable } from "./utils";
10
- import { getElementsInAtomicUnit, resizeElement } from "./utils";
10
+ import { getElementsInAtomicUnit } from "./utils";
11
11
  import { MIN_WIDTH_OR_HEIGHT } from "../../constants";
12
12
  import { pointFrom } from "../../../math";
13
13
  const STEP_SIZE = 10;
@@ -64,7 +64,6 @@ const resizeGroup = (nextWidth, nextHeight, initialHeight, aspectRatio, anchor,
64
64
  };
65
65
  const handleDimensionChange = ({ accumulatedChange, originalElements, originalElementsMap, originalAppState, shouldChangeByStepSize, nextValue, scene, property, }) => {
66
66
  const elementsMap = scene.getNonDeletedElementsMap();
67
- const elements = scene.getNonDeletedElements();
68
67
  const atomicUnits = getAtomicUnits(originalElements, originalAppState);
69
68
  if (nextValue !== undefined) {
70
69
  for (const atomicUnit of atomicUnits) {
@@ -109,7 +108,9 @@ const handleDimensionChange = ({ accumulatedChange, originalElements, originalEl
109
108
  }
110
109
  nextWidth = Math.max(MIN_WIDTH_OR_HEIGHT, nextWidth);
111
110
  nextHeight = Math.max(MIN_WIDTH_OR_HEIGHT, nextHeight);
112
- resizeElement(nextWidth, nextHeight, false, origElement, elementsMap, elements, scene, false);
111
+ resizeSingleElement(nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElementsMap, property === "width" ? "e" : "s", {
112
+ shouldInformMutation: false,
113
+ });
113
114
  }
114
115
  }
115
116
  }
@@ -176,7 +177,9 @@ const handleDimensionChange = ({ accumulatedChange, originalElements, originalEl
176
177
  }
177
178
  nextWidth = Math.max(MIN_WIDTH_OR_HEIGHT, nextWidth);
178
179
  nextHeight = Math.max(MIN_WIDTH_OR_HEIGHT, nextHeight);
179
- resizeElement(nextWidth, nextHeight, false, origElement, elementsMap, elements, scene);
180
+ resizeSingleElement(nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElementsMap, property === "width" ? "e" : "s", {
181
+ shouldInformMutation: false,
182
+ });
180
183
  }
181
184
  }
182
185
  }
@@ -1,9 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import StatsDragInput from "./DragInput";
3
3
  import { getStepSizedValue, moveElement } from "./utils";
4
- import { pointFrom, pointRotateRads } from "../../../math";
4
+ import { clamp, pointFrom, pointRotateRads, round } from "../../../math";
5
+ import { isImageElement } from "../../element/typeChecks";
6
+ import { getFlipAdjustedCropPosition, getUncroppedWidthAndHeight, } from "../../element/cropElement";
7
+ import { mutateElement } from "../../element/mutateElement";
5
8
  const STEP_SIZE = 10;
6
- const handlePositionChange = ({ accumulatedChange, originalElements, originalElementsMap, shouldChangeByStepSize, nextValue, property, scene, }) => {
9
+ const handlePositionChange = ({ accumulatedChange, instantChange, originalElements, originalElementsMap, shouldChangeByStepSize, nextValue, property, scene, originalAppState, }) => {
7
10
  const elementsMap = scene.getNonDeletedElementsMap();
8
11
  const elements = scene.getNonDeletedElements();
9
12
  const origElement = originalElements[0];
@@ -12,6 +15,55 @@ const handlePositionChange = ({ accumulatedChange, originalElements, originalEle
12
15
  origElement.y + origElement.height / 2,
13
16
  ];
14
17
  const [topLeftX, topLeftY] = pointRotateRads(pointFrom(origElement.x, origElement.y), pointFrom(cx, cy), origElement.angle);
18
+ if (originalAppState.croppingElementId === origElement.id) {
19
+ const element = elementsMap.get(origElement.id);
20
+ if (!element || !isImageElement(element) || !element.crop) {
21
+ return;
22
+ }
23
+ const crop = element.crop;
24
+ let nextCrop = crop;
25
+ const isFlippedByX = element.scale[0] === -1;
26
+ const isFlippedByY = element.scale[1] === -1;
27
+ const { width: uncroppedWidth, height: uncroppedHeight } = getUncroppedWidthAndHeight(element);
28
+ if (nextValue !== undefined) {
29
+ if (property === "x") {
30
+ const nextValueInNatural = nextValue * (crop.naturalWidth / uncroppedWidth);
31
+ if (isFlippedByX) {
32
+ nextCrop = {
33
+ ...crop,
34
+ x: clamp(crop.naturalWidth - nextValueInNatural - crop.width, 0, crop.naturalWidth - crop.width),
35
+ };
36
+ }
37
+ else {
38
+ nextCrop = {
39
+ ...crop,
40
+ x: clamp(nextValue * (crop.naturalWidth / uncroppedWidth), 0, crop.naturalWidth - crop.width),
41
+ };
42
+ }
43
+ }
44
+ if (property === "y") {
45
+ nextCrop = {
46
+ ...crop,
47
+ y: clamp(nextValue * (crop.naturalHeight / uncroppedHeight), 0, crop.naturalHeight - crop.height),
48
+ };
49
+ }
50
+ mutateElement(element, {
51
+ crop: nextCrop,
52
+ });
53
+ return;
54
+ }
55
+ const changeInX = (property === "x" ? instantChange : 0) * (isFlippedByX ? -1 : 1);
56
+ const changeInY = (property === "y" ? instantChange : 0) * (isFlippedByY ? -1 : 1);
57
+ nextCrop = {
58
+ ...crop,
59
+ x: clamp(crop.x + changeInX, 0, crop.naturalWidth - crop.width),
60
+ y: clamp(crop.y + changeInY, 0, crop.naturalHeight - crop.height),
61
+ };
62
+ mutateElement(element, {
63
+ crop: nextCrop,
64
+ });
65
+ return;
66
+ }
15
67
  if (nextValue !== undefined) {
16
68
  const newTopLeftX = property === "x" ? nextValue : topLeftX;
17
69
  const newTopLeftY = property === "y" ? nextValue : topLeftY;
@@ -34,7 +86,15 @@ const handlePositionChange = ({ accumulatedChange, originalElements, originalEle
34
86
  };
35
87
  const Position = ({ property, element, elementsMap, scene, appState, }) => {
36
88
  const [topLeftX, topLeftY] = pointRotateRads(pointFrom(element.x, element.y), pointFrom(element.x + element.width / 2, element.y + element.height / 2), element.angle);
37
- const value = Math.round((property === "x" ? topLeftX : topLeftY) * 100) / 100;
89
+ let value = round(property === "x" ? topLeftX : topLeftY, 2);
90
+ if (appState.croppingElementId === element.id &&
91
+ isImageElement(element) &&
92
+ element.crop) {
93
+ const flipAdjustedPosition = getFlipAdjustedCropPosition(element);
94
+ if (flipAdjustedPosition) {
95
+ value = round(property === "x" ? flipAdjustedPosition.x : flipAdjustedPosition.y, 2);
96
+ }
97
+ }
38
98
  return (_jsx(StatsDragInput, { label: property === "x" ? "X" : "Y", elements: [element], dragInputCallback: handlePositionChange, scene: scene, value: value, property: property, appState: appState }));
39
99
  };
40
100
  export default Position;
@@ -18,11 +18,14 @@ import Collapsible from "./Collapsible";
18
18
  import { useExcalidrawAppState, useExcalidrawSetAppState } from "../App";
19
19
  import { getAtomicUnits } from "./utils";
20
20
  import { STATS_PANELS } from "../../constants";
21
- import { isElbowArrow } from "../../element/typeChecks";
21
+ import { isElbowArrow, isImageElement } from "../../element/typeChecks";
22
22
  import CanvasGrid from "./CanvasGrid";
23
23
  import clsx from "clsx";
24
24
  import "./Stats.scss";
25
25
  import { isGridModeEnabled } from "../../snapping";
26
+ import { getUncroppedWidthAndHeight } from "../../element/cropElement";
27
+ import { round } from "../../../math";
28
+ import { frameAndChildrenSelectedTogether } from "../../frame";
26
29
  const STATS_TIMEOUT = 50;
27
30
  export const Stats = (props) => {
28
31
  const appState = useExcalidrawAppState();
@@ -50,6 +53,10 @@ export const StatsInner = memo(({ app, onClose, renderCustomStats, selectedEleme
50
53
  const setAppState = useExcalidrawSetAppState();
51
54
  const singleElement = selectedElements.length === 1 ? selectedElements[0] : null;
52
55
  const multipleElements = selectedElements.length > 1 ? selectedElements : null;
56
+ const cropMode = appState.croppingElementId && isImageElement(singleElement);
57
+ const unCroppedDimension = cropMode
58
+ ? getUncroppedWidthAndHeight(singleElement)
59
+ : null;
53
60
  const [sceneDimension, setSceneDimension] = useState({
54
61
  width: 0,
55
62
  height: 0,
@@ -68,6 +75,9 @@ export const StatsInner = memo(({ app, onClose, renderCustomStats, selectedEleme
68
75
  const atomicUnits = useMemo(() => {
69
76
  return getAtomicUnits(selectedElements, appState);
70
77
  }, [selectedElements, appState]);
78
+ const _frameAndChildrenSelectedTogether = useMemo(() => {
79
+ return frameAndChildrenSelectedTogether(selectedElements);
80
+ }, [selectedElements]);
71
81
  return (_jsx("div", { className: "exc-stats", children: _jsxs(Island, { padding: 3, children: [_jsxs("div", { className: "title", children: [_jsx("h2", { children: t("stats.title") }), _jsx("div", { className: "close", onClick: onClose, children: CloseIcon })] }), _jsxs(Collapsible, { label: _jsx("h3", { children: t("stats.generalStats") }), open: !!(appState.stats.panels & STATS_PANELS.generalStats), openTrigger: () => setAppState((state) => {
72
82
  return {
73
83
  stats: {
@@ -75,7 +85,7 @@ export const StatsInner = memo(({ app, onClose, renderCustomStats, selectedEleme
75
85
  panels: state.stats.panels ^ STATS_PANELS.generalStats,
76
86
  },
77
87
  };
78
- }), children: [_jsxs(StatsRows, { children: [_jsx(StatsRow, { heading: true, children: t("stats.scene") }), _jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.shapes") }), _jsx("div", { children: elements.length })] }), _jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.width") }), _jsx("div", { children: sceneDimension.width })] }), _jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.height") }), _jsx("div", { children: sceneDimension.height })] }), gridModeEnabled && (_jsxs(_Fragment, { children: [_jsx(StatsRow, { heading: true, children: "Canvas" }), _jsx(StatsRow, { children: _jsx(CanvasGrid, { property: "gridStep", scene: scene, appState: appState, setAppState: setAppState }) })] }))] }), renderCustomStats?.(elements, appState)] }), selectedElements.length > 0 && (_jsx("div", { id: "elementStats", style: {
88
+ }), children: [_jsxs(StatsRows, { children: [_jsx(StatsRow, { heading: true, children: t("stats.scene") }), _jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.shapes") }), _jsx("div", { children: elements.length })] }), _jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.width") }), _jsx("div", { children: sceneDimension.width })] }), _jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.height") }), _jsx("div", { children: sceneDimension.height })] }), gridModeEnabled && (_jsxs(_Fragment, { children: [_jsx(StatsRow, { heading: true, children: "Canvas" }), _jsx(StatsRow, { children: _jsx(CanvasGrid, { property: "gridStep", scene: scene, appState: appState, setAppState: setAppState }) })] }))] }), renderCustomStats?.(elements, appState)] }), !_frameAndChildrenSelectedTogether && selectedElements.length > 0 && (_jsx("div", { id: "elementStats", style: {
79
89
  marginTop: 12,
80
90
  }, children: _jsx(Collapsible, { label: _jsx("h3", { children: t("stats.elementProperties") }), open: !!(appState.stats.panels & STATS_PANELS.elementProperties), openTrigger: () => setAppState((state) => {
81
91
  return {
@@ -84,11 +94,18 @@ export const StatsInner = memo(({ app, onClose, renderCustomStats, selectedEleme
84
94
  panels: state.stats.panels ^ STATS_PANELS.elementProperties,
85
95
  },
86
96
  };
87
- }), children: _jsxs(StatsRows, { children: [singleElement && (_jsxs(_Fragment, { children: [_jsx(StatsRow, { heading: true, "data-testid": "stats-element-type", children: t(`element.${singleElement.type}`) }), _jsx(StatsRow, { children: _jsx(Position, { element: singleElement, property: "x", elementsMap: elementsMap, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(Position, { element: singleElement, property: "y", elementsMap: elementsMap, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(Dimension, { property: "width", element: singleElement, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(Dimension, { property: "height", element: singleElement, scene: scene, appState: appState }) }), !isElbowArrow(singleElement) && (_jsx(StatsRow, { children: _jsx(Angle, { property: "angle", element: singleElement, scene: scene, appState: appState }) })), _jsx(StatsRow, { children: _jsx(FontSize, { property: "fontSize", element: singleElement, scene: scene, appState: appState }) })] })), multipleElements && (_jsxs(_Fragment, { children: [elementsAreInSameGroup(multipleElements) && (_jsx(StatsRow, { heading: true, children: t("element.group") })), _jsxs(StatsRow, { columns: 2, style: { margin: "0.3125rem 0" }, children: [_jsx("div", { children: t("stats.shapes") }), _jsx("div", { children: selectedElements.length })] }), _jsx(StatsRow, { children: _jsx(MultiPosition, { property: "x", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiPosition, { property: "y", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiDimension, { property: "width", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiDimension, { property: "height", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiAngle, { property: "angle", elements: multipleElements, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiFontSize, { property: "fontSize", elements: multipleElements, scene: scene, appState: appState, elementsMap: elementsMap }) })] }))] }) }) }))] }) }));
97
+ }), children: _jsxs(StatsRows, { children: [singleElement && (_jsxs(_Fragment, { children: [cropMode && (_jsx(StatsRow, { heading: true, children: t("labels.unCroppedDimension") })), appState.croppingElementId &&
98
+ isImageElement(singleElement) &&
99
+ unCroppedDimension && (_jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.width") }), _jsx("div", { children: round(unCroppedDimension.width, 2) })] })), appState.croppingElementId &&
100
+ isImageElement(singleElement) &&
101
+ unCroppedDimension && (_jsxs(StatsRow, { columns: 2, children: [_jsx("div", { children: t("stats.height") }), _jsx("div", { children: round(unCroppedDimension.height, 2) })] })), _jsx(StatsRow, { heading: true, "data-testid": "stats-element-type", children: appState.croppingElementId
102
+ ? t("labels.imageCropping")
103
+ : t(`element.${singleElement.type}`) }), _jsx(StatsRow, { children: _jsx(Position, { element: singleElement, property: "x", elementsMap: elementsMap, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(Position, { element: singleElement, property: "y", elementsMap: elementsMap, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(Dimension, { property: "width", element: singleElement, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(Dimension, { property: "height", element: singleElement, scene: scene, appState: appState }) }), !isElbowArrow(singleElement) && (_jsx(StatsRow, { children: _jsx(Angle, { property: "angle", element: singleElement, scene: scene, appState: appState }) })), _jsx(StatsRow, { children: _jsx(FontSize, { property: "fontSize", element: singleElement, scene: scene, appState: appState }) })] })), multipleElements && (_jsxs(_Fragment, { children: [elementsAreInSameGroup(multipleElements) && (_jsx(StatsRow, { heading: true, children: t("element.group") })), _jsxs(StatsRow, { columns: 2, style: { margin: "0.3125rem 0" }, children: [_jsx("div", { children: t("stats.shapes") }), _jsx("div", { children: selectedElements.length })] }), _jsx(StatsRow, { children: _jsx(MultiPosition, { property: "x", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiPosition, { property: "y", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiDimension, { property: "width", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiDimension, { property: "height", elements: multipleElements, elementsMap: elementsMap, atomicUnits: atomicUnits, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiAngle, { property: "angle", elements: multipleElements, scene: scene, appState: appState }) }), _jsx(StatsRow, { children: _jsx(MultiFontSize, { property: "fontSize", elements: multipleElements, scene: scene, appState: appState, elementsMap: elementsMap }) })] }))] }) }) }))] }) }));
88
104
  }, (prev, next) => {
89
105
  return (prev.sceneNonce === next.sceneNonce &&
90
106
  prev.selectedElements === next.selectedElements &&
91
107
  prev.appState.stats.panels === next.appState.stats.panels &&
92
108
  prev.gridModeEnabled === next.gridModeEnabled &&
93
- prev.appState.gridStep === next.appState.gridStep);
109
+ prev.appState.gridStep === next.appState.gridStep &&
110
+ prev.appState.croppingElementId === next.appState.croppingElementId);
94
111
  });
@@ -14,7 +14,6 @@ export declare const newOrigin: (x1: number, y1: number, w1: number, h1: number,
14
14
  x: number;
15
15
  y: number;
16
16
  };
17
- export declare const resizeElement: (nextWidth: number, nextHeight: number, keepAspectRatio: boolean, origElement: ExcalidrawElement, elementsMap: NonDeletedSceneElementsMap, elements: readonly NonDeletedExcalidrawElement[], scene: Scene, shouldInformMutation?: boolean) => void;
18
17
  export declare const moveElement: (newTopLeftX: number, newTopLeftY: number, originalElement: ExcalidrawElement, elementsMap: NonDeletedSceneElementsMap, elements: readonly NonDeletedExcalidrawElement[], scene: Scene, originalElementsMap: ElementsMap, shouldInformMutation?: boolean) => void;
19
18
  export declare const getAtomicUnits: (targetElements: readonly ExcalidrawElement[], appState: AppState) => AtomicUnit[];
20
19
  export declare const updateBindings: (latestElement: ExcalidrawElement, elementsMap: NonDeletedSceneElementsMap, elements: readonly NonDeletedExcalidrawElement[], scene: Scene, options?: {
@@ -23,4 +22,5 @@ export declare const updateBindings: (latestElement: ExcalidrawElement, elements
23
22
  width: number;
24
23
  height: number;
25
24
  };
25
+ zoom?: AppState["zoom"];
26
26
  }) => void;