@excalidraw/excalidraw 0.17.1-1ed53b1 → 0.17.1-2f9526d

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 (76) hide show
  1. package/dist/browser/dev/excalidraw-assets-dev/{chunk-JKPJV7MZ.js → chunk-Q6A4M3MN.js} +4 -2
  2. package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6A4M3MN.js.map +7 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/{chunk-OKAZAA6U.js → chunk-Q6NFAEKN.js} +156 -74
  4. package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6NFAEKN.js.map +7 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/{dist-ITJNUBZF.js → dist-6QVAH5JA.js} +36 -14
  6. package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +7 -0
  7. package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js → en-Y27YPU72.js} +2 -2
  8. package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js → image-Y5X7K6KW.js} +2 -2
  9. package/dist/browser/dev/index.js +159 -86
  10. package/dist/browser/dev/index.js.map +3 -3
  11. package/dist/browser/prod/excalidraw-assets/{chunk-O4AI3NNG.js → chunk-IZMZ6RPD.js} +1 -1
  12. package/dist/browser/prod/excalidraw-assets/chunk-MDMKPHYD.js +55 -0
  13. package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +7 -0
  14. package/dist/browser/prod/excalidraw-assets/{en-N7CLNF6C.js → en-GSUSWMSH.js} +1 -1
  15. package/dist/browser/prod/excalidraw-assets/image-7MVXYJUE.js +1 -0
  16. package/dist/browser/prod/index.js +14 -14
  17. package/dist/dev/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
  18. package/dist/dev/index.js +320 -157
  19. package/dist/dev/index.js.map +4 -4
  20. package/dist/excalidraw/actions/actionBoundText.js +3 -1
  21. package/dist/excalidraw/actions/actionHistory.js +4 -4
  22. package/dist/excalidraw/actions/actionProperties.js +1 -1
  23. package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
  24. package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
  25. package/dist/excalidraw/actions/types.d.ts +1 -1
  26. package/dist/excalidraw/components/Actions.js +1 -1
  27. package/dist/excalidraw/components/App.d.ts +1 -1
  28. package/dist/excalidraw/components/App.js +47 -32
  29. package/dist/excalidraw/components/ButtonIconSelect.js +1 -1
  30. package/dist/excalidraw/components/CheckboxItem.js +1 -1
  31. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +2 -2
  32. package/dist/excalidraw/components/ContextMenu.js +1 -1
  33. package/dist/excalidraw/components/Dialog.js +1 -1
  34. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  35. package/dist/excalidraw/components/IconPicker.js +2 -2
  36. package/dist/excalidraw/components/LayerUI.js +2 -2
  37. package/dist/excalidraw/components/MobileMenu.js +1 -1
  38. package/dist/excalidraw/components/PasteChartDialog.js +1 -1
  39. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +1 -1
  40. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +2 -2
  41. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  42. package/dist/excalidraw/components/canvases/StaticCanvas.js +2 -2
  43. package/dist/excalidraw/components/icons.js +6 -2
  44. package/dist/excalidraw/data/restore.js +1 -0
  45. package/dist/excalidraw/element/index.d.ts +1 -1
  46. package/dist/excalidraw/element/index.js +1 -1
  47. package/dist/excalidraw/element/mutateElement.d.ts +1 -1
  48. package/dist/excalidraw/element/mutateElement.js +5 -3
  49. package/dist/excalidraw/element/newElement.d.ts +0 -5
  50. package/dist/excalidraw/element/newElement.js +15 -13
  51. package/dist/excalidraw/element/resizeElements.js +71 -22
  52. package/dist/excalidraw/element/resizeTest.js +2 -4
  53. package/dist/excalidraw/element/textElement.js +8 -3
  54. package/dist/excalidraw/element/textWysiwyg.d.ts +8 -3
  55. package/dist/excalidraw/element/textWysiwyg.js +6 -8
  56. package/dist/excalidraw/element/transformHandles.js +0 -10
  57. package/dist/excalidraw/element/types.d.ts +7 -0
  58. package/dist/excalidraw/locales/en.json +3 -1
  59. package/dist/excalidraw/scene/Fonts.d.ts +1 -3
  60. package/dist/excalidraw/scene/Fonts.js +6 -12
  61. package/dist/excalidraw/scene/Renderer.d.ts +1 -1
  62. package/dist/excalidraw/scene/Renderer.js +2 -3
  63. package/dist/excalidraw/scene/Scene.d.ts +10 -4
  64. package/dist/excalidraw/scene/Scene.js +14 -8
  65. package/dist/excalidraw/scene/export.js +1 -1
  66. package/dist/prod/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
  67. package/dist/prod/index.js +28 -28
  68. package/package.json +2 -2
  69. package/dist/browser/dev/excalidraw-assets-dev/chunk-JKPJV7MZ.js.map +0 -7
  70. package/dist/browser/dev/excalidraw-assets-dev/chunk-OKAZAA6U.js.map +0 -7
  71. package/dist/browser/dev/excalidraw-assets-dev/dist-ITJNUBZF.js.map +0 -7
  72. package/dist/browser/prod/excalidraw-assets/chunk-SXBDZOS3.js +0 -55
  73. package/dist/browser/prod/excalidraw-assets/dist-54276HPL.js +0 -6
  74. package/dist/browser/prod/excalidraw-assets/image-VAGBVQ3G.js +0 -1
  75. /package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js.map → en-Y27YPU72.js.map} +0 -0
  76. /package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js.map → image-Y5X7K6KW.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- import { BOUND_TEXT_PADDING, ROUNDNESS, VERTICAL_ALIGN, TEXT_ALIGN, } from "../constants";
1
+ import { BOUND_TEXT_PADDING, ROUNDNESS, TEXT_ALIGN, VERTICAL_ALIGN, } from "../constants";
2
2
  import { isTextElement, newElement } from "../element";
3
3
  import { mutateElement } from "../element/mutateElement";
4
4
  import { computeBoundTextPosition, computeContainerDimensionForBoundText, getBoundTextElement, measureText, redrawTextBoundingBox, } from "../element/textElement";
@@ -90,6 +90,7 @@ export const actionBindText = register({
90
90
  containerId: container.id,
91
91
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
92
92
  textAlign: TEXT_ALIGN.CENTER,
93
+ autoResize: true,
93
94
  });
94
95
  mutateElement(container, {
95
96
  boundElements: (container.boundElements || []).concat({
@@ -199,6 +200,7 @@ export const actionWrapTextInContainer = register({
199
200
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
200
201
  boundElements: null,
201
202
  textAlign: TEXT_ALIGN.CENTER,
203
+ autoResize: true,
202
204
  }, false);
203
205
  redrawTextBoundingBox(textElement, container, app.scene.getNonDeletedElementsMap());
204
206
  updatedElements = pushContainerBelowText([...updatedElements, container], container, textElement);
@@ -39,8 +39,8 @@ export const createUndoAction = (history, store) => ({
39
39
  event.key.toLowerCase() === KEYS.Z &&
40
40
  !event.shiftKey,
41
41
  PanelComponent: ({ updateData, data }) => {
42
- const { isUndoStackEmpty } = useEmitter(history.onHistoryChangedEmitter, new HistoryChangedEvent());
43
- return (_jsx(ToolButton, { type: "button", icon: UndoIcon, "aria-label": t("buttons.undo"), onClick: updateData, size: data?.size || "medium", disabled: isUndoStackEmpty }));
42
+ const { isUndoStackEmpty } = useEmitter(history.onHistoryChangedEmitter, new HistoryChangedEvent(history.isUndoStackEmpty, history.isRedoStackEmpty));
43
+ return (_jsx(ToolButton, { type: "button", icon: UndoIcon, "aria-label": t("buttons.undo"), onClick: updateData, size: data?.size || "medium", disabled: isUndoStackEmpty, "data-testid": "button-undo" }));
44
44
  },
45
45
  });
46
46
  export const createRedoAction = (history, store) => ({
@@ -56,7 +56,7 @@ export const createRedoAction = (history, store) => ({
56
56
  event.key.toLowerCase() === KEYS.Z) ||
57
57
  (isWindows && event.ctrlKey && !event.shiftKey && event.key === KEYS.Y),
58
58
  PanelComponent: ({ updateData, data }) => {
59
- const { isRedoStackEmpty } = useEmitter(history.onHistoryChangedEmitter, new HistoryChangedEvent());
60
- return (_jsx(ToolButton, { type: "button", icon: RedoIcon, "aria-label": t("buttons.redo"), onClick: updateData, size: data?.size || "medium", disabled: isRedoStackEmpty }));
59
+ const { isRedoStackEmpty } = useEmitter(history.onHistoryChangedEmitter, new HistoryChangedEvent(history.isUndoStackEmpty, history.isRedoStackEmpty));
60
+ return (_jsx(ToolButton, { type: "button", icon: RedoIcon, "aria-label": t("buttons.redo"), onClick: updateData, size: data?.size || "medium", disabled: isRedoStackEmpty, "data-testid": "button-redo" }));
61
61
  },
62
62
  });
@@ -60,7 +60,7 @@ export const getFormValue = function (elements, appState, getAttribute, isReleva
60
60
  return ret;
61
61
  };
62
62
  const offsetElementAfterFontResize = (prevElement, nextElement) => {
63
- if (isBoundToContainer(nextElement)) {
63
+ if (isBoundToContainer(nextElement) || !nextElement.autoResize) {
64
64
  return nextElement;
65
65
  }
66
66
  return mutateElement(nextElement, {
@@ -0,0 +1,17 @@
1
+ import type { AppClassProperties } from "../types";
2
+ export declare const actionTextAutoResize: {
3
+ name: "autoResize";
4
+ label: string;
5
+ icon: null;
6
+ trackEvent: {
7
+ category: "element";
8
+ };
9
+ predicate: (elements: readonly import("../element/types").ExcalidrawElement[], appState: import("../types").AppState, _: unknown, app: AppClassProperties) => boolean;
10
+ perform: (elements: readonly import("../element/types").OrderedExcalidrawElement[], appState: Readonly<import("../types").AppState>, _: any, app: AppClassProperties) => {
11
+ appState: Readonly<import("../types").AppState>;
12
+ elements: import("../element/types").OrderedExcalidrawElement[];
13
+ storeAction: import("../store").StoreActionType;
14
+ };
15
+ } & {
16
+ keyTest?: undefined;
17
+ };
@@ -0,0 +1,38 @@
1
+ import { isTextElement } from "../element";
2
+ import { newElementWith } from "../element/mutateElement";
3
+ import { measureText } from "../element/textElement";
4
+ import { getSelectedElements } from "../scene";
5
+ import { StoreAction } from "../store";
6
+ import { getFontString } from "../utils";
7
+ import { register } from "./register";
8
+ export const actionTextAutoResize = register({
9
+ name: "autoResize",
10
+ label: "labels.autoResize",
11
+ icon: null,
12
+ trackEvent: { category: "element" },
13
+ predicate: (elements, appState, _, app) => {
14
+ const selectedElements = getSelectedElements(elements, appState);
15
+ return (selectedElements.length === 1 &&
16
+ isTextElement(selectedElements[0]) &&
17
+ !selectedElements[0].autoResize);
18
+ },
19
+ perform: (elements, appState, _, app) => {
20
+ const selectedElements = getSelectedElements(elements, appState);
21
+ return {
22
+ appState,
23
+ elements: elements.map((element) => {
24
+ if (element.id === selectedElements[0].id && isTextElement(element)) {
25
+ const metrics = measureText(element.originalText, getFontString(element), element.lineHeight);
26
+ return newElementWith(element, {
27
+ autoResize: true,
28
+ width: metrics.width,
29
+ height: metrics.height,
30
+ text: element.originalText,
31
+ });
32
+ }
33
+ return element;
34
+ }),
35
+ storeAction: StoreAction.CAPTURE,
36
+ };
37
+ },
38
+ });
@@ -15,7 +15,7 @@ export type ActionResult = {
15
15
  type ActionFn = (elements: readonly OrderedExcalidrawElement[], appState: Readonly<AppState>, formData: any, app: AppClassProperties) => ActionResult | Promise<ActionResult>;
16
16
  export type UpdaterFn = (res: ActionResult) => void;
17
17
  export type ActionFilterFn = (action: Action) => void;
18
- export type ActionName = "copy" | "cut" | "paste" | "copyAsPng" | "copyAsSvg" | "copyText" | "sendBackward" | "bringForward" | "sendToBack" | "bringToFront" | "copyStyles" | "selectAll" | "pasteStyles" | "gridMode" | "zenMode" | "objectsSnapMode" | "stats" | "changeStrokeColor" | "changeBackgroundColor" | "changeFillStyle" | "changeStrokeWidth" | "changeStrokeShape" | "changeSloppiness" | "changeStrokeStyle" | "changeArrowhead" | "changeOpacity" | "changeFontSize" | "toggleCanvasMenu" | "toggleEditMenu" | "undo" | "redo" | "finalize" | "changeProjectName" | "changeExportBackground" | "changeExportEmbedScene" | "changeExportScale" | "saveToActiveFile" | "saveFileToDisk" | "loadScene" | "duplicateSelection" | "deleteSelectedElements" | "changeViewBackgroundColor" | "clearCanvas" | "zoomIn" | "zoomOut" | "resetZoom" | "zoomToFit" | "zoomToFitSelection" | "zoomToFitSelectionInViewport" | "changeFontFamily" | "changeTextAlign" | "changeVerticalAlign" | "toggleFullScreen" | "toggleShortcuts" | "group" | "ungroup" | "goToCollaborator" | "addToLibrary" | "changeRoundness" | "alignTop" | "alignBottom" | "alignLeft" | "alignRight" | "alignVerticallyCentered" | "alignHorizontallyCentered" | "distributeHorizontally" | "distributeVertically" | "flipHorizontal" | "flipVertical" | "viewMode" | "exportWithDarkMode" | "toggleTheme" | "increaseFontSize" | "decreaseFontSize" | "unbindText" | "hyperlink" | "bindText" | "unlockAllElements" | "toggleElementLock" | "toggleLinearEditor" | "toggleEraserTool" | "toggleHandTool" | "selectAllElementsInFrame" | "removeAllElementsFromFrame" | "updateFrameRendering" | "setFrameAsActiveTool" | "setEmbeddableAsActiveTool" | "createContainerFromText" | "wrapTextInContainer" | "commandPalette";
18
+ export type ActionName = "copy" | "cut" | "paste" | "copyAsPng" | "copyAsSvg" | "copyText" | "sendBackward" | "bringForward" | "sendToBack" | "bringToFront" | "copyStyles" | "selectAll" | "pasteStyles" | "gridMode" | "zenMode" | "objectsSnapMode" | "stats" | "changeStrokeColor" | "changeBackgroundColor" | "changeFillStyle" | "changeStrokeWidth" | "changeStrokeShape" | "changeSloppiness" | "changeStrokeStyle" | "changeArrowhead" | "changeOpacity" | "changeFontSize" | "toggleCanvasMenu" | "toggleEditMenu" | "undo" | "redo" | "finalize" | "changeProjectName" | "changeExportBackground" | "changeExportEmbedScene" | "changeExportScale" | "saveToActiveFile" | "saveFileToDisk" | "loadScene" | "duplicateSelection" | "deleteSelectedElements" | "changeViewBackgroundColor" | "clearCanvas" | "zoomIn" | "zoomOut" | "resetZoom" | "zoomToFit" | "zoomToFitSelection" | "zoomToFitSelectionInViewport" | "changeFontFamily" | "changeTextAlign" | "changeVerticalAlign" | "toggleFullScreen" | "toggleShortcuts" | "group" | "ungroup" | "goToCollaborator" | "addToLibrary" | "changeRoundness" | "alignTop" | "alignBottom" | "alignLeft" | "alignRight" | "alignVerticallyCentered" | "alignHorizontallyCentered" | "distributeHorizontally" | "distributeVertically" | "flipHorizontal" | "flipVertical" | "viewMode" | "exportWithDarkMode" | "toggleTheme" | "increaseFontSize" | "decreaseFontSize" | "unbindText" | "hyperlink" | "bindText" | "unlockAllElements" | "toggleElementLock" | "toggleLinearEditor" | "toggleEraserTool" | "toggleHandTool" | "selectAllElementsInFrame" | "removeAllElementsFromFrame" | "updateFrameRendering" | "setFrameAsActiveTool" | "setEmbeddableAsActiveTool" | "createContainerFromText" | "wrapTextInContainer" | "commandPalette" | "autoResize";
19
19
  export type PanelComponentProps = {
20
20
  elements: readonly ExcalidrawElement[];
21
21
  appState: AppState;
@@ -140,7 +140,7 @@ export const ShapesSwitcher = ({ activeTool, appState, app, UIOptions, }) => {
140
140
  };
141
141
  export const ZoomActions = ({ renderAction, zoom, }) => (_jsx(Stack.Col, { gap: 1, className: "zoom-actions", children: _jsxs(Stack.Row, { align: "center", children: [renderAction("zoomOut"), renderAction("resetZoom"), renderAction("zoomIn")] }) }));
142
142
  export const UndoRedoActions = ({ renderAction, className, }) => (_jsxs("div", { className: `undo-redo-buttons ${className}`, children: [_jsx("div", { className: "undo-button-container", children: _jsx(Tooltip, { label: t("buttons.undo"), children: renderAction("undo") }) }), _jsx("div", { className: "redo-button-container", children: _jsxs(Tooltip, { label: t("buttons.redo"), children: [" ", renderAction("redo")] }) })] }));
143
- export const ExitZenModeAction = ({ actionManager, showExitZenModeBtn, }) => (_jsx("button", { className: clsx("disable-zen-mode", {
143
+ export const ExitZenModeAction = ({ actionManager, showExitZenModeBtn, }) => (_jsx("button", { type: "button", className: clsx("disable-zen-mode", {
144
144
  "disable-zen-mode--visible": showExitZenModeBtn,
145
145
  }), onClick: () => actionManager.executeAction(actionToggleZenMode), children: t("buttons.exitZenMode") }));
146
146
  export const FinalizeAction = ({ renderAction, className, }) => (_jsx("div", { className: `finalize-button ${className}`, children: renderAction("finalize", { size: "small" }) }));
@@ -345,7 +345,7 @@ declare class App extends React.Component<AppProps, AppState> {
345
345
  /** @default StoreAction.CAPTURE */
346
346
  storeAction?: SceneData["storeAction"];
347
347
  }) => void;
348
- private onSceneUpdated;
348
+ private triggerRender;
349
349
  /**
350
350
  * @returns whether the menu was toggled on or off
351
351
  */
@@ -15,7 +15,7 @@ import { APP_NAME, CURSOR_TYPE, DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT, DEFAULT_VERTI
15
15
  import { exportCanvas, loadFromBlob } from "../data";
16
16
  import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
17
17
  import { restore, restoreElements } from "../data/restore";
18
- import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, updateTextElement, redrawTextBoundingBox, getElementAbsoluteCoords, } from "../element";
18
+ import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, refreshTextDimensions, redrawTextBoundingBox, getElementAbsoluteCoords, } from "../element";
19
19
  import { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, updateBoundElements, getSuggestedBindingsForArrows, } from "../element/binding";
20
20
  import { LinearElementEditor } from "../element/linearElementEditor";
21
21
  import { mutateElement, newElementWith } from "../element/mutateElement";
@@ -90,6 +90,7 @@ import { isOverScrollBars } from "../scene/scrollbars";
90
90
  import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
91
91
  import { isPointHittingLink, isPointHittingLinkIcon, } from "./hyperlink/helpers";
92
92
  import { getShortcutFromShortcutName } from "../actions/shortcuts";
93
+ import { actionTextAutoResize } from "../actions/actionTextAutoResize";
93
94
  const AppContext = React.createContext(null);
94
95
  const AppPropsContext = React.createContext(null);
95
96
  const deviceContextInitialValue = {
@@ -286,10 +287,7 @@ class App extends React.Component {
286
287
  container: this.excalidrawContainerRef.current,
287
288
  id: this.id,
288
289
  };
289
- this.fonts = new Fonts({
290
- scene: this.scene,
291
- onSceneUpdated: this.onSceneUpdated,
292
- });
290
+ this.fonts = new Fonts({ scene: this.scene });
293
291
  this.history = new History();
294
292
  this.actionManager.registerAll(actions);
295
293
  this.actionManager.registerAction(createUndoAction(this.history, this.store));
@@ -444,7 +442,7 @@ class App extends React.Component {
444
442
  return false;
445
443
  });
446
444
  if (updated) {
447
- this.scene.informMutation();
445
+ this.scene.triggerUpdate();
448
446
  }
449
447
  // GC
450
448
  this.iFrameRefs.forEach((ref, id) => {
@@ -803,9 +801,9 @@ class App extends React.Component {
803
801
  render() {
804
802
  const selectedElements = this.scene.getSelectedElements(this.state);
805
803
  const { renderTopRightUI, renderCustomStats } = this.props;
806
- const versionNonce = this.scene.getVersionNonce();
804
+ const sceneNonce = this.scene.getSceneNonce();
807
805
  const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
808
- versionNonce,
806
+ sceneNonce,
809
807
  zoom: this.state.zoom,
810
808
  offsetLeft: this.state.offsetLeft,
811
809
  offsetTop: this.state.offsetTop,
@@ -874,14 +872,14 @@ class App extends React.Component {
874
872
  this.focusContainer();
875
873
  callback?.();
876
874
  });
877
- } })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, versionNonce: versionNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
875
+ } })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
878
876
  imageCache: this.imageCache,
879
877
  isExporting: false,
880
878
  renderGrid: true,
881
879
  canvasBackgroundColor: this.state.viewBackgroundColor,
882
880
  embedsValidationStatus: this.embedsValidationStatus,
883
881
  elementsPendingErasure: this.elementsPendingErasure,
884
- } }), _jsx(InteractiveCanvas, { containerRef: this.excalidrawContainerRef, canvas: this.interactiveCanvas, elementsMap: elementsMap, visibleElements: visibleElements, selectedElements: selectedElements, versionNonce: versionNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, device: this.device, renderInteractiveSceneCallback: this.renderInteractiveSceneCallback, handleCanvasRef: this.handleInteractiveCanvasRef, onContextMenu: this.handleCanvasContextMenu, onPointerMove: this.handleCanvasPointerMove, onPointerUp: this.handleCanvasPointerUp, onPointerCancel: this.removePointer, onTouchMove: this.handleTouchMove, onPointerDown: this.handleCanvasPointerDown, onDoubleClick: this.handleCanvasDoubleClick }), this.state.userToFollow && (_jsx(FollowMode, { width: this.state.width, height: this.state.height, userToFollow: this.state.userToFollow, onDisconnect: this.maybeUnfollowRemoteUser })), this.renderFrameNames()] }), this.renderEmbeddables()] }) }) }) }) }) }) }) }));
882
+ } }), _jsx(InteractiveCanvas, { containerRef: this.excalidrawContainerRef, canvas: this.interactiveCanvas, elementsMap: elementsMap, visibleElements: visibleElements, selectedElements: selectedElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, device: this.device, renderInteractiveSceneCallback: this.renderInteractiveSceneCallback, handleCanvasRef: this.handleInteractiveCanvasRef, onContextMenu: this.handleCanvasContextMenu, onPointerMove: this.handleCanvasPointerMove, onPointerUp: this.handleCanvasPointerUp, onPointerCancel: this.removePointer, onTouchMove: this.handleTouchMove, onPointerDown: this.handleCanvasPointerDown, onDoubleClick: this.handleCanvasDoubleClick }), this.state.userToFollow && (_jsx(FollowMode, { width: this.state.width, height: this.state.height, userToFollow: this.state.userToFollow, onDisconnect: this.maybeUnfollowRemoteUser })), this.renderFrameNames()] }), this.renderEmbeddables()] }) }) }) }) }) }) }) }));
885
883
  }
886
884
  focusContainer = () => {
887
885
  this.excalidrawContainerRef.current?.focus();
@@ -931,7 +929,7 @@ class App extends React.Component {
931
929
  mutateElement(frameElement, { customData: { generationData: data } }, false);
932
930
  }
933
931
  this.magicGenerations.set(frameElement.id, data);
934
- this.onSceneUpdated();
932
+ this.triggerRender();
935
933
  };
936
934
  getTextFromElements(elements) {
937
935
  const text = elements
@@ -1441,7 +1439,7 @@ class App extends React.Component {
1441
1439
  this.store.onStoreIncrementEmitter.on((increment) => {
1442
1440
  this.history.record(increment.elementsChange, increment.appStateChange);
1443
1441
  });
1444
- this.scene.addCallback(this.onSceneUpdated);
1442
+ this.scene.onUpdate(this.triggerRender);
1445
1443
  this.addEventListeners();
1446
1444
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
1447
1445
  this.focusContainer();
@@ -1479,6 +1477,7 @@ class App extends React.Component {
1479
1477
  componentWillUnmount() {
1480
1478
  this.renderer.destroy();
1481
1479
  this.scene = new Scene();
1480
+ this.fonts = new Fonts({ scene: this.scene });
1482
1481
  this.renderer = new Renderer(this.scene);
1483
1482
  this.files = {};
1484
1483
  this.imageCache.clear();
@@ -1543,6 +1542,9 @@ class App extends React.Component {
1543
1542
  // Safari-only desktop pinch zoom
1544
1543
  addEventListener(document, EVENT.GESTURE_START, this.onGestureStart, false), addEventListener(document, EVENT.GESTURE_CHANGE, this.onGestureChange, false), addEventListener(document, EVENT.GESTURE_END, this.onGestureEnd, false), addEventListener(window, EVENT.FOCUS, () => {
1545
1544
  this.maybeCleanupAfterMissingPointerUp(null);
1545
+ // browsers (chrome?) tend to free up memory a lot, which results
1546
+ // in canvas context being cleared. Thus re-render on focus.
1547
+ this.triggerRender(true);
1546
1548
  }));
1547
1549
  if (this.state.viewModeEnabled) {
1548
1550
  return;
@@ -2292,7 +2294,7 @@ class App extends React.Component {
2292
2294
  ShapeCache.delete(element);
2293
2295
  }
2294
2296
  });
2295
- this.scene.informMutation();
2297
+ this.scene.triggerUpdate();
2296
2298
  this.addNewImagesToImageCache();
2297
2299
  });
2298
2300
  updateScene = withBatchedUpdates((sceneData) => {
@@ -2326,8 +2328,15 @@ class App extends React.Component {
2326
2328
  this.setState({ collaborators: sceneData.collaborators });
2327
2329
  }
2328
2330
  });
2329
- onSceneUpdated = () => {
2330
- this.setState({});
2331
+ triggerRender = (
2332
+ /** force always re-renders canvas even if no change */
2333
+ force) => {
2334
+ if (force === true) {
2335
+ this.scene.triggerUpdate();
2336
+ }
2337
+ else {
2338
+ this.setState({});
2339
+ }
2331
2340
  };
2332
2341
  /**
2333
2342
  * @returns whether the menu was toggled on or off
@@ -2772,15 +2781,16 @@ class App extends React.Component {
2772
2781
  });
2773
2782
  handleTextWysiwyg(element, { isExistingElement = false, }) {
2774
2783
  const elementsMap = this.scene.getElementsMapIncludingDeleted();
2775
- const updateElement = (text, originalText, isDeleted) => {
2784
+ const updateElement = (nextOriginalText, isDeleted) => {
2776
2785
  this.scene.replaceAllElements([
2777
2786
  // Not sure why we include deleted elements as well hence using deleted elements map
2778
2787
  ...this.scene.getElementsIncludingDeleted().map((_element) => {
2779
2788
  if (_element.id === element.id && isTextElement(_element)) {
2780
- return updateTextElement(_element, getContainerElement(_element, elementsMap), elementsMap, {
2781
- text,
2782
- isDeleted,
2783
- originalText,
2789
+ return newElementWith(_element, {
2790
+ originalText: nextOriginalText,
2791
+ isDeleted: isDeleted ?? _element.isDeleted,
2792
+ // returns (wrapped) text and new dimensions
2793
+ ...refreshTextDimensions(_element, getContainerElement(_element, elementsMap), elementsMap, nextOriginalText),
2784
2794
  });
2785
2795
  }
2786
2796
  return _element;
@@ -2800,15 +2810,15 @@ class App extends React.Component {
2800
2810
  viewportY - this.state.offsetTop,
2801
2811
  ];
2802
2812
  },
2803
- onChange: withBatchedUpdates((text) => {
2804
- updateElement(text, text, false);
2813
+ onChange: withBatchedUpdates((nextOriginalText) => {
2814
+ updateElement(nextOriginalText, false);
2805
2815
  if (isNonDeletedElement(element)) {
2806
2816
  updateBoundElements(element, elementsMap);
2807
2817
  }
2808
2818
  }),
2809
- onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
2810
- const isDeleted = !text.trim();
2811
- updateElement(text, originalText, isDeleted);
2819
+ onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
2820
+ const isDeleted = !nextOriginalText.trim();
2821
+ updateElement(nextOriginalText, isDeleted);
2812
2822
  // select the created text element only if submitting via keyboard
2813
2823
  // (when submitting via click it should act as signal to deselect)
2814
2824
  if (!isDeleted && viaKeyboard) {
@@ -2847,7 +2857,7 @@ class App extends React.Component {
2847
2857
  this.deselectElements();
2848
2858
  // do an initial update to re-initialize element position since we were
2849
2859
  // modifying element's x/y for sake of editor (case: syncing to remote)
2850
- updateElement(element.text, element.originalText, false);
2860
+ updateElement(element.originalText, false);
2851
2861
  }
2852
2862
  deselectElements() {
2853
2863
  this.setState({
@@ -3317,8 +3327,11 @@ class App extends React.Component {
3317
3327
  }, state);
3318
3328
  this.translateCanvas({
3319
3329
  zoom: zoomState.zoom,
3320
- scrollX: zoomState.scrollX + deltaX / nextZoom,
3321
- scrollY: zoomState.scrollY + deltaY / nextZoom,
3330
+ // 2x multiplier is just a magic number that makes this work correctly
3331
+ // on touchscreen devices (note: if we get report that panning is slower/faster
3332
+ // than actual movement, consider swapping with devicePixelRatio)
3333
+ scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
3334
+ scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
3322
3335
  shouldCacheIgnoreZoom: true,
3323
3336
  });
3324
3337
  });
@@ -3639,7 +3652,7 @@ class App extends React.Component {
3639
3652
  }
3640
3653
  }
3641
3654
  this.elementsPendingErasure = new Set(this.elementsPendingErasure);
3642
- this.onSceneUpdated();
3655
+ this.triggerRender();
3643
3656
  }
3644
3657
  };
3645
3658
  // set touch moving for mobile context menu
@@ -5323,7 +5336,7 @@ class App extends React.Component {
5323
5336
  groupIds: [],
5324
5337
  });
5325
5338
  removeElementsFromFrame([linearElement], this.scene.getNonDeletedElementsMap());
5326
- this.scene.informMutation();
5339
+ this.scene.triggerUpdate();
5327
5340
  }
5328
5341
  }
5329
5342
  }
@@ -5640,7 +5653,7 @@ class App extends React.Component {
5640
5653
  }
5641
5654
  restoreReadyToEraseElements = () => {
5642
5655
  this.elementsPendingErasure = new Set();
5643
- this.onSceneUpdated();
5656
+ this.triggerRender();
5644
5657
  };
5645
5658
  eraseElements = () => {
5646
5659
  let didChange = false;
@@ -5932,7 +5945,7 @@ class App extends React.Component {
5932
5945
  if (uncachedImageElements.length) {
5933
5946
  const { updatedFiles } = await this.updateImageCache(uncachedImageElements, files);
5934
5947
  if (updatedFiles.size) {
5935
- this.scene.informMutation();
5948
+ this.scene.triggerUpdate();
5936
5949
  }
5937
5950
  }
5938
5951
  };
@@ -6346,6 +6359,7 @@ class App extends React.Component {
6346
6359
  return [actionCopy, ...options];
6347
6360
  }
6348
6361
  return [
6362
+ CONTEXT_MENU_SEPARATOR,
6349
6363
  actionCut,
6350
6364
  actionCopy,
6351
6365
  actionPaste,
@@ -6358,6 +6372,7 @@ class App extends React.Component {
6358
6372
  actionPasteStyles,
6359
6373
  CONTEXT_MENU_SEPARATOR,
6360
6374
  actionGroup,
6375
+ actionTextAutoResize,
6361
6376
  actionUnbindText,
6362
6377
  actionBindText,
6363
6378
  actionWrapTextInContainer,
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import clsx from "clsx";
3
3
  // TODO: It might be "clever" to add option.icon to the existing component <ButtonSelect />
4
- export const ButtonIconSelect = (props) => (_jsx("div", { className: "buttonList buttonListIcon", children: props.options.map((option) => props.type === "button" ? (_jsx("button", { onClick: (event) => props.onClick(option.value, event), className: clsx({
4
+ export const ButtonIconSelect = (props) => (_jsx("div", { className: "buttonList buttonListIcon", children: props.options.map((option) => props.type === "button" ? (_jsx("button", { type: "button", onClick: (event) => props.onClick(option.value, event), className: clsx({
5
5
  active: option.active ?? props.value === option.value,
6
6
  }), "data-testid": option.testId, title: option.text, children: option.icon }, option.text)) : (_jsxs("label", { className: clsx({ active: props.value === option.value }), title: option.text, children: [_jsx("input", { type: "radio", name: props.group, onChange: () => props.onChange(option.value), checked: props.value === option.value, "data-testid": option.testId }), option.icon] }, option.text))) }));
@@ -6,5 +6,5 @@ export const CheckboxItem = ({ children, checked, onChange, className }) => {
6
6
  return (_jsxs("div", { className: clsx("Checkbox", className, { "is-checked": checked }), onClick: (event) => {
7
7
  onChange(!checked, event);
8
8
  event.currentTarget.querySelector(".Checkbox-box").focus();
9
- }, children: [_jsx("button", { className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }), _jsx("div", { className: "Checkbox-label", children: children })] }));
9
+ }, children: [_jsx("button", { type: "button", className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }), _jsx("div", { className: "Checkbox-label", children: children })] }));
10
10
  };
@@ -416,7 +416,7 @@ function CommandPaletteInner({ customCommandPaletteItems, }) {
416
416
  ...command,
417
417
  icon: command.icon || boltIcon,
418
418
  order: command.order ?? getCategoryOrder(command.category),
419
- haystack: `${deburr(command.label)} ${command.keywords?.join(" ") || ""}`,
419
+ haystack: `${deburr(command.label.toLocaleLowerCase())} ${command.keywords?.join(" ") || ""}`,
420
420
  };
421
421
  });
422
422
  setAllCommands(allCommands);
@@ -584,7 +584,7 @@ function CommandPaletteInner({ customCommandPaletteItems, }) {
584
584
  setCurrentCommand(showLastUsed ? lastUsed : matchingCommands[0] || null);
585
585
  return;
586
586
  }
587
- const _query = deburr(commandSearch.replace(/[<>-_| ]/g, ""));
587
+ const _query = deburr(commandSearch.toLocaleLowerCase().replace(/[<>_| -]/g, ""));
588
588
  matchingCommands = fuzzy
589
589
  .filter(_query, matchingCommands, {
590
590
  extract: (command) => command.haystack,
@@ -46,7 +46,7 @@ export const ContextMenu = React.memo(({ actionManager, items, top, left, onClos
46
46
  onClose(() => {
47
47
  actionManager.executeAction(item, "contextMenu");
48
48
  });
49
- }, children: _jsxs("button", { className: clsx("context-menu-item", {
49
+ }, children: _jsxs("button", { type: "button", className: clsx("context-menu-item", {
50
50
  dangerous: actionName === "deleteSelectedElements",
51
51
  checkmark: item.checked?.(appState),
52
52
  }), children: [_jsx("div", { className: "context-menu-item__label", children: label }), _jsx("kbd", { className: "context-menu-item__shortcut", children: actionName
@@ -72,5 +72,5 @@ export const Dialog = (props) => {
72
72
  };
73
73
  return (_jsx(Modal, { className: clsx("Dialog", props.className, {
74
74
  "Dialog--fullscreen": isFullscreen,
75
- }), labelledBy: "dialog-title", maxWidth: getDialogSize(props.size), onCloseRequest: onClose, closeOnClickOutside: props.closeOnClickOutside, children: _jsxs(Island, { ref: setIslandNode, children: [props.title && (_jsx("h2", { id: `${id}-dialog-title`, className: "Dialog__title", children: _jsx("span", { className: "Dialog__titleContent", children: props.title }) })), isFullscreen && (_jsx("button", { className: "Dialog__close", onClick: onClose, title: t("buttons.close"), "aria-label": t("buttons.close"), children: CloseIcon })), _jsx("div", { className: "Dialog__content", children: props.children })] }) }));
75
+ }), labelledBy: "dialog-title", maxWidth: getDialogSize(props.size), onCloseRequest: onClose, closeOnClickOutside: props.closeOnClickOutside, children: _jsxs(Island, { ref: setIslandNode, children: [props.title && (_jsx("h2", { id: `${id}-dialog-title`, className: "Dialog__title", children: _jsx("span", { className: "Dialog__titleContent", children: props.title }) })), isFullscreen && (_jsx("button", { className: "Dialog__close", onClick: onClose, title: t("buttons.close"), "aria-label": t("buttons.close"), type: "button", children: CloseIcon })), _jsx("div", { className: "Dialog__content", children: props.children })] }) }));
76
76
  };
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { CloseIcon } from "../icons";
3
3
  import "./FollowMode.scss";
4
4
  const FollowMode = ({ height, width, userToFollow, onDisconnect, }) => {
5
- return (_jsx("div", { className: "follow-mode", style: { width, height }, children: _jsxs("div", { className: "follow-mode__badge", children: [_jsxs("div", { className: "follow-mode__badge__label", children: ["Following", " ", _jsx("span", { className: "follow-mode__badge__username", title: userToFollow.username, children: userToFollow.username })] }), _jsx("button", { onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })] }) }));
5
+ return (_jsx("div", { className: "follow-mode", style: { width, height }, children: _jsxs("div", { className: "follow-mode__badge", children: [_jsxs("div", { className: "follow-mode__badge__label", children: ["Following", " ", _jsx("span", { className: "follow-mode__badge__username", title: userToFollow.username, children: userToFollow.username })] }), _jsx("button", { type: "button", onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })] }) }));
6
6
  };
7
7
  export default FollowMode;
@@ -72,7 +72,7 @@ function Picker({ options, value, label, onChange, onClose, }) {
72
72
  event.nativeEvent.stopImmediatePropagation();
73
73
  event.stopPropagation();
74
74
  };
75
- return (_jsx("div", { className: `picker`, role: "dialog", "aria-modal": "true", "aria-label": label, onKeyDown: handleKeyDown, children: _jsx("div", { className: "picker-content", ref: rGallery, children: options.map((option, i) => (_jsxs("button", { className: clsx("picker-option", {
75
+ return (_jsx("div", { className: `picker`, role: "dialog", "aria-modal": "true", "aria-label": label, onKeyDown: handleKeyDown, children: _jsx("div", { className: "picker-content", ref: rGallery, children: options.map((option, i) => (_jsxs("button", { type: "button", className: clsx("picker-option", {
76
76
  active: value === option.value,
77
77
  }), onClick: (event) => {
78
78
  event.currentTarget.focus();
@@ -92,7 +92,7 @@ export function IconPicker({ value, label, options, onChange, group = "", }) {
92
92
  const [isActive, setActive] = React.useState(false);
93
93
  const rPickerButton = React.useRef(null);
94
94
  const isRTL = getLanguage().rtl;
95
- return (_jsxs("div", { children: [_jsx("button", { name: group, className: isActive ? "active" : "", "aria-label": label, onClick: () => setActive(!isActive), ref: rPickerButton, children: options.find((option) => option.value === value)?.icon }), _jsx(React.Suspense, { fallback: "", children: isActive ? (_jsxs(_Fragment, { children: [_jsx(Popover, { onCloseRequest: (event) => event.target !== rPickerButton.current && setActive(false), ...(isRTL ? { right: 5.5 } : { left: -5.5 }), children: _jsx(Picker, { options: options.filter((opt) => opt.showInPicker !== false), value: value, label: label, onChange: onChange, onClose: () => {
95
+ return (_jsxs("div", { children: [_jsx("button", { name: group, type: "button", className: isActive ? "active" : "", "aria-label": label, onClick: () => setActive(!isActive), ref: rPickerButton, children: options.find((option) => option.value === value)?.icon }), _jsx(React.Suspense, { fallback: "", children: isActive ? (_jsxs(_Fragment, { children: [_jsx(Popover, { onCloseRequest: (event) => event.target !== rPickerButton.current && setActive(false), ...(isRTL ? { right: 5.5 } : { left: -5.5 }), children: _jsx(Picker, { options: options.filter((opt) => opt.showInPicker !== false), value: value, label: label, onChange: onChange, onClose: () => {
96
96
  setActive(false);
97
97
  rPickerButton.current?.focus();
98
98
  } }) }), _jsx("div", { className: "picker-triangle" })] })) : null })] }));
@@ -126,7 +126,7 @@ const LayerUI = ({ actionManager, appState, files, setAppState, elements, canvas
126
126
  }, false);
127
127
  ShapeCache.delete(element);
128
128
  }
129
- Scene.getScene(selectedElements[0])?.informMutation();
129
+ Scene.getScene(selectedElements[0])?.triggerUpdate();
130
130
  }
131
131
  else if (colorPickerType === "elementBackground") {
132
132
  setAppState({
@@ -160,7 +160,7 @@ const LayerUI = ({ actionManager, appState, files, setAppState, elements, canvas
160
160
  ? { width: `calc(100% - ${LIBRARY_SIDEBAR_WIDTH}px)` }
161
161
  : {}, children: [renderWelcomeScreen && _jsx(tunnels.WelcomeScreenCenterTunnel.Out, {}), renderFixedSideContainer(), _jsx(Footer, { appState: appState, actionManager: actionManager, showExitZenModeBtn: showExitZenModeBtn, renderWelcomeScreen: renderWelcomeScreen }), appState.showStats && (_jsx(Stats, { appState: appState, setAppState: setAppState, elements: elements, onClose: () => {
162
162
  actionManager.executeAction(actionToggleStats);
163
- }, renderCustomStats: renderCustomStats })), appState.scrolledOutside && (_jsx("button", { className: "scroll-back-to-content", onClick: () => {
163
+ }, renderCustomStats: renderCustomStats })), appState.scrolledOutside && (_jsx("button", { type: "button", className: "scroll-back-to-content", onClick: () => {
164
164
  setAppState((appState) => ({
165
165
  ...calculateScrollCenter(elements, appState),
166
166
  }));
@@ -37,7 +37,7 @@ export const MobileMenu = ({ appState, elements, actionManager, setAppState, onL
37
37
  !appState.viewModeEnabled &&
38
38
  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
39
  !appState.openMenu &&
40
- !appState.openSidebar && (_jsx("button", { className: "scroll-back-to-content", onClick: () => {
40
+ !appState.openSidebar && (_jsx("button", { type: "button", className: "scroll-back-to-content", onClick: () => {
41
41
  setAppState((appState) => ({
42
42
  ...calculateScrollCenter(elements, appState),
43
43
  }));
@@ -35,7 +35,7 @@ const ChartPreviewBtn = (props) => {
35
35
  previewNode.replaceChildren();
36
36
  };
37
37
  }, [props.spreadsheet, props.chartType, props.selected]);
38
- return (_jsx("button", { className: "ChartPreview", onClick: () => {
38
+ return (_jsx("button", { type: "button", className: "ChartPreview", onClick: () => {
39
39
  if (chartElements) {
40
40
  props.onClick(props.chartType, chartElements);
41
41
  }
@@ -9,7 +9,7 @@ type InteractiveCanvasProps = {
9
9
  elementsMap: RenderableElementsMap;
10
10
  visibleElements: readonly NonDeletedExcalidrawElement[];
11
11
  selectedElements: readonly NonDeletedExcalidrawElement[];
12
- versionNonce: number | undefined;
12
+ sceneNonce: number | undefined;
13
13
  selectionNonce: number | undefined;
14
14
  scale: number;
15
15
  appState: InteractiveCanvasAppState;
@@ -103,10 +103,10 @@ const getRelevantAppStateProps = (appState) => ({
103
103
  const areEqual = (prevProps, nextProps) => {
104
104
  // This could be further optimised if needed, as we don't have to render interactive canvas on each scene mutation
105
105
  if (prevProps.selectionNonce !== nextProps.selectionNonce ||
106
- prevProps.versionNonce !== nextProps.versionNonce ||
106
+ prevProps.sceneNonce !== nextProps.sceneNonce ||
107
107
  prevProps.scale !== nextProps.scale ||
108
108
  // we need to memoize on elementsMap because they may have renewed
109
- // even if versionNonce didn't change (e.g. we filter elements out based
109
+ // even if sceneNonce didn't change (e.g. we filter elements out based
110
110
  // on appState)
111
111
  prevProps.elementsMap !== nextProps.elementsMap ||
112
112
  prevProps.visibleElements !== nextProps.visibleElements ||
@@ -9,7 +9,7 @@ type StaticCanvasProps = {
9
9
  elementsMap: RenderableElementsMap;
10
10
  allElementsMap: NonDeletedSceneElementsMap;
11
11
  visibleElements: readonly NonDeletedExcalidrawElement[];
12
- versionNonce: number | undefined;
12
+ sceneNonce: number | undefined;
13
13
  selectionNonce: number | undefined;
14
14
  scale: number;
15
15
  appState: StaticCanvasAppState;
@@ -70,10 +70,10 @@ const getRelevantAppStateProps = (appState) => ({
70
70
  editingGroupId: appState.editingGroupId,
71
71
  });
72
72
  const areEqual = (prevProps, nextProps) => {
73
- if (prevProps.versionNonce !== nextProps.versionNonce ||
73
+ if (prevProps.sceneNonce !== nextProps.sceneNonce ||
74
74
  prevProps.scale !== nextProps.scale ||
75
75
  // we need to memoize on elementsMap because they may have renewed
76
- // even if versionNonce didn't change (e.g. we filter elements out based
76
+ // even if sceneNonce didn't change (e.g. we filter elements out based
77
77
  // on appState)
78
78
  prevProps.elementsMap !== nextProps.elementsMap ||
79
79
  prevProps.visibleElements !== nextProps.visibleElements) {
@@ -126,12 +126,16 @@ const arrownNarrowUpJSX = (_jsxs("g", { strokeWidth: 1.5, children: [_jsx("path"
126
126
  export const BringForwardIcon = createIcon(arrownNarrowUpJSX, tablerIconProps);
127
127
  export const SendBackwardIcon = createIcon(arrownNarrowUpJSX, {
128
128
  ...tablerIconProps,
129
- transform: "rotate(180)",
129
+ style: {
130
+ transform: "rotate(180deg)",
131
+ },
130
132
  });
131
133
  export const BringToFrontIcon = createIcon(arrowBarToTopJSX, tablerIconProps);
132
134
  export const SendToBackIcon = createIcon(arrowBarToTopJSX, {
133
135
  ...tablerIconProps,
134
- transform: "rotate(180)",
136
+ style: {
137
+ transform: "rotate(180deg)",
138
+ },
135
139
  });
136
140
  //
137
141
  // Align action icons created from scratch to match those of z-index actions
@@ -124,6 +124,7 @@ const restoreElement = (element) => {
124
124
  verticalAlign: element.verticalAlign || DEFAULT_VERTICAL_ALIGN,
125
125
  containerId: element.containerId ?? null,
126
126
  originalText: element.originalText || text,
127
+ autoResize: element.autoResize ?? true,
127
128
  lineHeight,
128
129
  });
129
130
  // if empty text, mark as deleted. We keep in array
@@ -1,5 +1,5 @@
1
1
  import type { ExcalidrawElement, NonDeletedExcalidrawElement, NonDeleted } from "./types";
2
- export { newElement, newTextElement, updateTextElement, refreshTextDimensions, newLinearElement, newImageElement, duplicateElement, } from "./newElement";
2
+ export { newElement, newTextElement, refreshTextDimensions, newLinearElement, newImageElement, duplicateElement, } from "./newElement";
3
3
  export { getElementAbsoluteCoords, getElementBounds, getCommonBounds, getDiamondPoints, getArrowheadPoints, getClosestElementBounds, } from "./bounds";
4
4
  export { OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, getTransformHandlesFromCoords, getTransformHandles, } from "./transformHandles";
5
5
  export { resizeTest, getCursorForResizingElement, getElementWithTransformHandleType, getTransformHandleTypeFromCoords, } from "./resizeTest";