@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.
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-JKPJV7MZ.js → chunk-Q6A4M3MN.js} +4 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6A4M3MN.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-OKAZAA6U.js → chunk-Q6NFAEKN.js} +156 -74
- package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6NFAEKN.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{dist-ITJNUBZF.js → dist-6QVAH5JA.js} +36 -14
- package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js → en-Y27YPU72.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js → image-Y5X7K6KW.js} +2 -2
- package/dist/browser/dev/index.js +159 -86
- package/dist/browser/dev/index.js.map +3 -3
- package/dist/browser/prod/excalidraw-assets/{chunk-O4AI3NNG.js → chunk-IZMZ6RPD.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/chunk-MDMKPHYD.js +55 -0
- package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +7 -0
- package/dist/browser/prod/excalidraw-assets/{en-N7CLNF6C.js → en-GSUSWMSH.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/image-7MVXYJUE.js +1 -0
- package/dist/browser/prod/index.js +14 -14
- package/dist/dev/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
- package/dist/dev/index.js +320 -157
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionBoundText.js +3 -1
- package/dist/excalidraw/actions/actionHistory.js +4 -4
- package/dist/excalidraw/actions/actionProperties.js +1 -1
- package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
- package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/components/Actions.js +1 -1
- package/dist/excalidraw/components/App.d.ts +1 -1
- package/dist/excalidraw/components/App.js +47 -32
- package/dist/excalidraw/components/ButtonIconSelect.js +1 -1
- package/dist/excalidraw/components/CheckboxItem.js +1 -1
- package/dist/excalidraw/components/CommandPalette/CommandPalette.js +2 -2
- package/dist/excalidraw/components/ContextMenu.js +1 -1
- package/dist/excalidraw/components/Dialog.js +1 -1
- package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
- package/dist/excalidraw/components/IconPicker.js +2 -2
- package/dist/excalidraw/components/LayerUI.js +2 -2
- package/dist/excalidraw/components/MobileMenu.js +1 -1
- package/dist/excalidraw/components/PasteChartDialog.js +1 -1
- package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +1 -1
- package/dist/excalidraw/components/canvases/InteractiveCanvas.js +2 -2
- package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
- package/dist/excalidraw/components/canvases/StaticCanvas.js +2 -2
- package/dist/excalidraw/components/icons.js +6 -2
- package/dist/excalidraw/data/restore.js +1 -0
- package/dist/excalidraw/element/index.d.ts +1 -1
- package/dist/excalidraw/element/index.js +1 -1
- package/dist/excalidraw/element/mutateElement.d.ts +1 -1
- package/dist/excalidraw/element/mutateElement.js +5 -3
- package/dist/excalidraw/element/newElement.d.ts +0 -5
- package/dist/excalidraw/element/newElement.js +15 -13
- package/dist/excalidraw/element/resizeElements.js +71 -22
- package/dist/excalidraw/element/resizeTest.js +2 -4
- package/dist/excalidraw/element/textElement.js +8 -3
- package/dist/excalidraw/element/textWysiwyg.d.ts +8 -3
- package/dist/excalidraw/element/textWysiwyg.js +6 -8
- package/dist/excalidraw/element/transformHandles.js +0 -10
- package/dist/excalidraw/element/types.d.ts +7 -0
- package/dist/excalidraw/locales/en.json +3 -1
- package/dist/excalidraw/scene/Fonts.d.ts +1 -3
- package/dist/excalidraw/scene/Fonts.js +6 -12
- package/dist/excalidraw/scene/Renderer.d.ts +1 -1
- package/dist/excalidraw/scene/Renderer.js +2 -3
- package/dist/excalidraw/scene/Scene.d.ts +10 -4
- package/dist/excalidraw/scene/Scene.js +14 -8
- package/dist/excalidraw/scene/export.js +1 -1
- package/dist/prod/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
- package/dist/prod/index.js +28 -28
- package/package.json +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-JKPJV7MZ.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-OKAZAA6U.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/dist-ITJNUBZF.js.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-SXBDZOS3.js +0 -55
- package/dist/browser/prod/excalidraw-assets/dist-54276HPL.js +0 -6
- package/dist/browser/prod/excalidraw-assets/image-VAGBVQ3G.js +0 -1
- /package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js.map → en-Y27YPU72.js.map} +0 -0
- /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,
|
|
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
|
|
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,
|
|
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.
|
|
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
|
|
804
|
+
const sceneNonce = this.scene.getSceneNonce();
|
|
807
805
|
const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
|
|
808
|
-
|
|
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,
|
|
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,
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
2330
|
-
|
|
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 = (
|
|
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
|
|
2781
|
-
|
|
2782
|
-
isDeleted,
|
|
2783
|
-
|
|
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((
|
|
2804
|
-
updateElement(
|
|
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(({
|
|
2810
|
-
const isDeleted = !
|
|
2811
|
-
updateElement(
|
|
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.
|
|
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
|
-
|
|
3321
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(/[
|
|
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])?.
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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";
|