@excalidraw/excalidraw 0.17.1-1ed53b1 → 0.17.1-22b3927
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-VC7RRIDZ.js} +230 -93
- package/dist/browser/dev/excalidraw-assets-dev/chunk-VC7RRIDZ.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-J7S3ALXP.js} +2 -2
- package/dist/browser/dev/index.js +335 -116
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/chunk-CWO763YJ.js +55 -0
- package/dist/browser/prod/excalidraw-assets/{chunk-O4AI3NNG.js → chunk-IZMZ6RPD.js} +1 -1
- 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-SZBFRCU2.js +1 -0
- package/dist/browser/prod/index.js +24 -24
- package/dist/dev/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
- package/dist/dev/index.js +576 -207
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionAddToLibrary.d.ts +3 -3
- package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
- package/dist/excalidraw/actions/actionBoundText.d.ts +3 -3
- package/dist/excalidraw/actions/actionBoundText.js +3 -1
- package/dist/excalidraw/actions/actionCanvas.d.ts +13 -13
- package/dist/excalidraw/actions/actionClipboard.d.ts +12 -12
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
- package/dist/excalidraw/actions/actionDistribute.d.ts +2 -2
- package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +1 -1
- package/dist/excalidraw/actions/actionElementLock.d.ts +2 -2
- package/dist/excalidraw/actions/actionExport.d.ts +11 -11
- package/dist/excalidraw/actions/actionFinalize.d.ts +2 -2
- package/dist/excalidraw/actions/actionFlip.d.ts +2 -2
- package/dist/excalidraw/actions/actionFrame.d.ts +312 -4
- package/dist/excalidraw/actions/actionGroup.d.ts +312 -2
- package/dist/excalidraw/actions/actionHistory.js +4 -4
- package/dist/excalidraw/actions/actionLinearEditor.d.ts +1 -1
- package/dist/excalidraw/actions/actionLink.d.ts +1 -1
- package/dist/excalidraw/actions/actionMenu.d.ts +3 -3
- package/dist/excalidraw/actions/actionNavigate.d.ts +2 -2
- package/dist/excalidraw/actions/actionProperties.d.ts +13 -13
- package/dist/excalidraw/actions/actionProperties.js +1 -1
- package/dist/excalidraw/actions/actionSelectAll.d.ts +1 -1
- package/dist/excalidraw/actions/actionStyles.d.ts +5 -2
- package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
- package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
- package/dist/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleStats.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleZenMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionZindex.d.ts +4 -4
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/change.js +13 -6
- package/dist/excalidraw/components/Actions.js +1 -1
- package/dist/excalidraw/components/App.d.ts +2 -2
- package/dist/excalidraw/components/App.js +133 -51
- 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 +3 -2
- package/dist/excalidraw/components/canvases/InteractiveCanvas.js +4 -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/constants.d.ts +1 -0
- package/dist/excalidraw/constants.js +5 -0
- package/dist/excalidraw/data/restore.js +3 -0
- package/dist/excalidraw/element/dragElements.d.ts +2 -2
- package/dist/excalidraw/element/dragElements.js +27 -3
- package/dist/excalidraw/element/embeddable.d.ts +1 -1
- 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 +2 -5
- package/dist/excalidraw/element/newElement.js +16 -14
- package/dist/excalidraw/element/resizeElements.js +73 -21
- package/dist/excalidraw/element/resizeTest.js +2 -4
- package/dist/excalidraw/element/textElement.d.ts +1 -0
- package/dist/excalidraw/element/textElement.js +11 -3
- package/dist/excalidraw/element/textWysiwyg.d.ts +10 -4
- package/dist/excalidraw/element/textWysiwyg.js +38 -17
- package/dist/excalidraw/element/transformHandles.js +0 -10
- package/dist/excalidraw/element/types.d.ts +7 -0
- package/dist/excalidraw/fractionalIndex.js +2 -4
- package/dist/excalidraw/locales/en.json +3 -1
- package/dist/excalidraw/mermaid.d.ts +2 -0
- package/dist/excalidraw/mermaid.js +28 -0
- package/dist/excalidraw/renderer/interactiveScene.d.ts +1 -1
- package/dist/excalidraw/renderer/interactiveScene.js +31 -5
- package/dist/excalidraw/renderer/renderElement.d.ts +2 -2
- package/dist/excalidraw/renderer/renderElement.js +2 -2
- 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/excalidraw/scene/types.d.ts +2 -1
- package/dist/excalidraw/snapping.js +2 -1
- package/dist/excalidraw/store.d.ts +32 -2
- package/dist/excalidraw/store.js +27 -0
- package/dist/excalidraw/types.d.ts +1 -0
- package/dist/prod/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
- package/dist/prod/index.js +42 -42
- 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-J7S3ALXP.js.map} +0 -0
|
@@ -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
|
|
@@ -10,6 +10,7 @@ export declare const isIOS: boolean;
|
|
|
10
10
|
export declare const isBrave: () => boolean;
|
|
11
11
|
export declare const supportsResizeObserver: boolean;
|
|
12
12
|
export declare const APP_NAME = "Excalidraw";
|
|
13
|
+
export declare const TEXT_AUTOWRAP_THRESHOLD = 36;
|
|
13
14
|
export declare const DRAGGING_THRESHOLD = 10;
|
|
14
15
|
export declare const LINE_CONFIRM_THRESHOLD = 8;
|
|
15
16
|
export declare const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
|
|
@@ -15,6 +15,11 @@ export const isIOS = /iPad|iPhone/.test(navigator.platform) ||
|
|
|
15
15
|
export const isBrave = () => navigator.brave?.isBrave?.name === "isBrave";
|
|
16
16
|
export const supportsResizeObserver = typeof window !== "undefined" && "ResizeObserver" in window;
|
|
17
17
|
export const APP_NAME = "Excalidraw";
|
|
18
|
+
// distance when creating text before it's considered `autoResize: false`
|
|
19
|
+
// we're using higher threshold so that clicks that end up being drags
|
|
20
|
+
// don't unintentionally create text elements that are wrapped to a few chars
|
|
21
|
+
// (happens a lot with fast clicks with the text tool)
|
|
22
|
+
export const TEXT_AUTOWRAP_THRESHOLD = 36; // px
|
|
18
23
|
export const DRAGGING_THRESHOLD = 10; // px
|
|
19
24
|
export const LINE_CONFIRM_THRESHOLD = 8; // px
|
|
20
25
|
export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
|
|
@@ -10,6 +10,7 @@ import { arrayToMap } from "../utils";
|
|
|
10
10
|
import { detectLineHeight, getContainerElement, getDefaultLineHeight, } from "../element/textElement";
|
|
11
11
|
import { normalizeLink } from "./url";
|
|
12
12
|
import { syncInvalidIndices } from "../fractionalIndex";
|
|
13
|
+
import { getSizeFromPoints } from "../points";
|
|
13
14
|
export const AllowedExcalidrawActiveTools = {
|
|
14
15
|
selection: true,
|
|
15
16
|
text: true,
|
|
@@ -124,6 +125,7 @@ const restoreElement = (element) => {
|
|
|
124
125
|
verticalAlign: element.verticalAlign || DEFAULT_VERTICAL_ALIGN,
|
|
125
126
|
containerId: element.containerId ?? null,
|
|
126
127
|
originalText: element.originalText || text,
|
|
128
|
+
autoResize: element.autoResize ?? true,
|
|
127
129
|
lineHeight,
|
|
128
130
|
});
|
|
129
131
|
// if empty text, mark as deleted. We keep in array
|
|
@@ -177,6 +179,7 @@ const restoreElement = (element) => {
|
|
|
177
179
|
points,
|
|
178
180
|
x,
|
|
179
181
|
y,
|
|
182
|
+
...getSizeFromPoints(points),
|
|
180
183
|
});
|
|
181
184
|
}
|
|
182
185
|
// generic elements
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NonDeletedExcalidrawElement } from "./types";
|
|
2
|
-
import type { AppState, PointerDownState } from "../types";
|
|
2
|
+
import type { AppState, NormalizedZoomValue, PointerDownState } from "../types";
|
|
3
3
|
import type Scene from "../scene/Scene";
|
|
4
4
|
export declare const dragSelectedElements: (pointerDownState: PointerDownState, selectedElements: NonDeletedExcalidrawElement[], offset: {
|
|
5
5
|
x: number;
|
|
@@ -9,7 +9,7 @@ export declare const dragSelectedElements: (pointerDownState: PointerDownState,
|
|
|
9
9
|
y: number;
|
|
10
10
|
}, gridSize: AppState["gridSize"]) => void;
|
|
11
11
|
export declare const getDragOffsetXY: (selectedElements: NonDeletedExcalidrawElement[], x: number, y: number) => [number, number];
|
|
12
|
-
export declare const dragNewElement: (draggingElement: NonDeletedExcalidrawElement, elementType: AppState["activeTool"]["type"], originX: number, originY: number, x: number, y: number, width: number, height: number, shouldMaintainAspectRatio: boolean, shouldResizeFromCenter: boolean, widthAspectRatio?: number | null, originOffset?: {
|
|
12
|
+
export declare const dragNewElement: (draggingElement: NonDeletedExcalidrawElement, elementType: AppState["activeTool"]["type"], originX: number, originY: number, x: number, y: number, width: number, height: number, shouldMaintainAspectRatio: boolean, shouldResizeFromCenter: boolean, zoom: NormalizedZoomValue, widthAspectRatio?: number | null, originOffset?: {
|
|
13
13
|
x: number;
|
|
14
14
|
y: number;
|
|
15
15
|
} | null) => void;
|
|
@@ -2,9 +2,11 @@ import { updateBoundElements } from "./binding";
|
|
|
2
2
|
import { getCommonBounds } from "./bounds";
|
|
3
3
|
import { mutateElement } from "./mutateElement";
|
|
4
4
|
import { getPerfectElementSize } from "./sizeHelpers";
|
|
5
|
-
import { getBoundTextElement } from "./textElement";
|
|
5
|
+
import { getBoundTextElement, getMinTextElementWidth } from "./textElement";
|
|
6
6
|
import { getGridPoint } from "../math";
|
|
7
|
-
import { isArrowElement, isFrameLikeElement } from "./typeChecks";
|
|
7
|
+
import { isArrowElement, isFrameLikeElement, isTextElement, } from "./typeChecks";
|
|
8
|
+
import { getFontString } from "../utils";
|
|
9
|
+
import { TEXT_AUTOWRAP_THRESHOLD } from "../constants";
|
|
8
10
|
export const dragSelectedElements = (pointerDownState, selectedElements, offset, appState, scene, snapOffset, gridSize) => {
|
|
9
11
|
// we do not want a frame and its elements to be selected at the same time
|
|
10
12
|
// but when it happens (due to some bug), we want to avoid updating element
|
|
@@ -68,7 +70,7 @@ export const getDragOffsetXY = (selectedElements, x, y) => {
|
|
|
68
70
|
const [x1, y1] = getCommonBounds(selectedElements);
|
|
69
71
|
return [x - x1, y - y1];
|
|
70
72
|
};
|
|
71
|
-
export const dragNewElement = (draggingElement, elementType, originX, originY, x, y, width, height, shouldMaintainAspectRatio, shouldResizeFromCenter,
|
|
73
|
+
export const dragNewElement = (draggingElement, elementType, originX, originY, x, y, width, height, shouldMaintainAspectRatio, shouldResizeFromCenter, zoom,
|
|
72
74
|
/** whether to keep given aspect ratio when `isResizeWithSidesSameLength` is
|
|
73
75
|
true */
|
|
74
76
|
widthAspectRatio, originOffset = null) => {
|
|
@@ -99,12 +101,34 @@ widthAspectRatio, originOffset = null) => {
|
|
|
99
101
|
newX = originX - width / 2;
|
|
100
102
|
newY = originY - height / 2;
|
|
101
103
|
}
|
|
104
|
+
let textAutoResize = null;
|
|
105
|
+
// NOTE this should apply only to creating text elements, not existing
|
|
106
|
+
// (once we rewrite appState.draggingElement to actually mean dragging
|
|
107
|
+
// elements)
|
|
108
|
+
if (isTextElement(draggingElement)) {
|
|
109
|
+
height = draggingElement.height;
|
|
110
|
+
const minWidth = getMinTextElementWidth(getFontString({
|
|
111
|
+
fontSize: draggingElement.fontSize,
|
|
112
|
+
fontFamily: draggingElement.fontFamily,
|
|
113
|
+
}), draggingElement.lineHeight);
|
|
114
|
+
width = Math.max(width, minWidth);
|
|
115
|
+
if (Math.abs(x - originX) > TEXT_AUTOWRAP_THRESHOLD / zoom) {
|
|
116
|
+
textAutoResize = {
|
|
117
|
+
autoResize: false,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
newY = originY;
|
|
121
|
+
if (shouldResizeFromCenter) {
|
|
122
|
+
newX = originX - width / 2;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
102
125
|
if (width !== 0 && height !== 0) {
|
|
103
126
|
mutateElement(draggingElement, {
|
|
104
127
|
x: newX + (originOffset?.x ?? 0),
|
|
105
128
|
y: newY + (originOffset?.y ?? 0),
|
|
106
129
|
width,
|
|
107
130
|
height,
|
|
131
|
+
...textAutoResize,
|
|
108
132
|
});
|
|
109
133
|
}
|
|
110
134
|
};
|
|
@@ -167,7 +167,7 @@ export declare const actionSetEmbeddableAsActiveTool: {
|
|
|
167
167
|
userToFollow: import("../types").UserToFollow | null;
|
|
168
168
|
followedBy: Set<import("../types").SocketId>;
|
|
169
169
|
};
|
|
170
|
-
storeAction:
|
|
170
|
+
storeAction: "none";
|
|
171
171
|
};
|
|
172
172
|
} & {
|
|
173
173
|
keyTest?: undefined;
|
|
@@ -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";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isInvisiblySmallElement } from "./sizeHelpers";
|
|
2
2
|
import { isLinearElementType } from "./typeChecks";
|
|
3
|
-
export { newElement, newTextElement,
|
|
3
|
+
export { newElement, newTextElement, refreshTextDimensions, newLinearElement, newImageElement, duplicateElement, } from "./newElement";
|
|
4
4
|
export { getElementAbsoluteCoords, getElementBounds, getCommonBounds, getDiamondPoints, getArrowheadPoints, getClosestElementBounds, } from "./bounds";
|
|
5
5
|
export { OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, getTransformHandlesFromCoords, getTransformHandles, } from "./transformHandles";
|
|
6
6
|
export { resizeTest, getCursorForResizingElement, getElementWithTransformHandleType, getTransformHandleTypeFromCoords, } from "./resizeTest";
|
|
@@ -2,7 +2,7 @@ import type { ExcalidrawElement } from "./types";
|
|
|
2
2
|
import type { Mutable } from "../utility-types";
|
|
3
3
|
export type ElementUpdate<TElement extends ExcalidrawElement> = Omit<Partial<TElement>, "id" | "version" | "versionNonce" | "updated">;
|
|
4
4
|
export declare const mutateElement: <TElement extends Mutable<ExcalidrawElement>>(element: TElement, updates: ElementUpdate<TElement>, informMutation?: boolean) => TElement;
|
|
5
|
-
export declare const newElementWith: <TElement extends ExcalidrawElement>(element: TElement, updates: ElementUpdate<TElement
|
|
5
|
+
export declare const newElementWith: <TElement extends ExcalidrawElement>(element: TElement, updates: ElementUpdate<TElement>, force?: boolean) => TElement;
|
|
6
6
|
/**
|
|
7
7
|
* Mutates element, bumping `version`, `versionNonce`, and `updated`.
|
|
8
8
|
*
|
|
@@ -71,11 +71,13 @@ export const mutateElement = (element, updates, informMutation = true) => {
|
|
|
71
71
|
element.versionNonce = randomInteger();
|
|
72
72
|
element.updated = getUpdatedTimestamp();
|
|
73
73
|
if (informMutation) {
|
|
74
|
-
Scene.getScene(element)?.
|
|
74
|
+
Scene.getScene(element)?.triggerUpdate();
|
|
75
75
|
}
|
|
76
76
|
return element;
|
|
77
77
|
};
|
|
78
|
-
export const newElementWith = (element, updates
|
|
78
|
+
export const newElementWith = (element, updates,
|
|
79
|
+
/** pass `true` to always regenerate */
|
|
80
|
+
force = false) => {
|
|
79
81
|
let didChange = false;
|
|
80
82
|
for (const key in updates) {
|
|
81
83
|
const value = updates[key];
|
|
@@ -88,7 +90,7 @@ export const newElementWith = (element, updates) => {
|
|
|
88
90
|
didChange = true;
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
|
-
if (!didChange) {
|
|
93
|
+
if (!didChange && !force) {
|
|
92
94
|
return element;
|
|
93
95
|
}
|
|
94
96
|
return {
|
|
@@ -19,6 +19,7 @@ export declare const newMagicFrameElement: (opts: {
|
|
|
19
19
|
} & ElementConstructorOpts) => NonDeleted<ExcalidrawMagicFrameElement>;
|
|
20
20
|
export declare const newTextElement: (opts: {
|
|
21
21
|
text: string;
|
|
22
|
+
originalText?: string;
|
|
22
23
|
fontSize?: number;
|
|
23
24
|
fontFamily?: FontFamilyValues;
|
|
24
25
|
textAlign?: TextAlign;
|
|
@@ -26,6 +27,7 @@ export declare const newTextElement: (opts: {
|
|
|
26
27
|
containerId?: ExcalidrawTextContainer["id"] | null;
|
|
27
28
|
lineHeight?: ExcalidrawTextElement["lineHeight"];
|
|
28
29
|
strokeWidth?: ExcalidrawTextElement["strokeWidth"];
|
|
30
|
+
autoResize?: ExcalidrawTextElement["autoResize"];
|
|
29
31
|
} & ElementConstructorOpts) => NonDeleted<ExcalidrawTextElement>;
|
|
30
32
|
export declare const refreshTextDimensions: (textElement: ExcalidrawTextElement, container: ExcalidrawTextContainer | null, elementsMap: ElementsMap, text?: string) => {
|
|
31
33
|
x: number;
|
|
@@ -34,11 +36,6 @@ export declare const refreshTextDimensions: (textElement: ExcalidrawTextElement,
|
|
|
34
36
|
height: number;
|
|
35
37
|
text: string;
|
|
36
38
|
} | undefined;
|
|
37
|
-
export declare const updateTextElement: (textElement: ExcalidrawTextElement, container: ExcalidrawTextContainer | null, elementsMap: ElementsMap, { text, isDeleted, originalText, }: {
|
|
38
|
-
text: string;
|
|
39
|
-
isDeleted?: boolean | undefined;
|
|
40
|
-
originalText: string;
|
|
41
|
-
}) => ExcalidrawTextElement;
|
|
42
39
|
export declare const newFreeDrawElement: (opts: {
|
|
43
40
|
type: "freedraw";
|
|
44
41
|
points?: ExcalidrawFreeDrawElement["points"];
|
|
@@ -85,7 +85,7 @@ export const newTextElement = (opts) => {
|
|
|
85
85
|
const textAlign = opts.textAlign || DEFAULT_TEXT_ALIGN;
|
|
86
86
|
const verticalAlign = opts.verticalAlign || DEFAULT_VERTICAL_ALIGN;
|
|
87
87
|
const offsets = getTextElementPositionOffsets({ textAlign, verticalAlign }, metrics);
|
|
88
|
-
const
|
|
88
|
+
const textElementProps = {
|
|
89
89
|
..._newElementBase("text", opts),
|
|
90
90
|
text,
|
|
91
91
|
fontSize,
|
|
@@ -97,19 +97,26 @@ export const newTextElement = (opts) => {
|
|
|
97
97
|
width: metrics.width,
|
|
98
98
|
height: metrics.height,
|
|
99
99
|
containerId: opts.containerId || null,
|
|
100
|
-
originalText: text,
|
|
100
|
+
originalText: opts.originalText ?? text,
|
|
101
|
+
autoResize: opts.autoResize ?? true,
|
|
101
102
|
lineHeight,
|
|
102
|
-
}
|
|
103
|
+
};
|
|
104
|
+
const textElement = newElementWith(textElementProps, {});
|
|
103
105
|
return textElement;
|
|
104
106
|
};
|
|
105
107
|
const getAdjustedDimensions = (element, elementsMap, nextText) => {
|
|
106
|
-
|
|
108
|
+
let { width: nextWidth, height: nextHeight } = measureText(nextText, getFontString(element), element.lineHeight);
|
|
109
|
+
// wrapped text
|
|
110
|
+
if (!element.autoResize) {
|
|
111
|
+
nextWidth = element.width;
|
|
112
|
+
}
|
|
107
113
|
const { textAlign, verticalAlign } = element;
|
|
108
114
|
let x;
|
|
109
115
|
let y;
|
|
110
116
|
if (textAlign === "center" &&
|
|
111
117
|
verticalAlign === VERTICAL_ALIGN.MIDDLE &&
|
|
112
|
-
!element.containerId
|
|
118
|
+
!element.containerId &&
|
|
119
|
+
element.autoResize) {
|
|
113
120
|
const prevMetrics = measureText(element.text, getFontString(element), element.lineHeight);
|
|
114
121
|
const offsets = getTextElementPositionOffsets(element, {
|
|
115
122
|
width: nextWidth - prevMetrics.width,
|
|
@@ -142,19 +149,14 @@ export const refreshTextDimensions = (textElement, container, elementsMap, text
|
|
|
142
149
|
if (textElement.isDeleted) {
|
|
143
150
|
return;
|
|
144
151
|
}
|
|
145
|
-
if (container) {
|
|
146
|
-
text = wrapText(text, getFontString(textElement),
|
|
152
|
+
if (container || !textElement.autoResize) {
|
|
153
|
+
text = wrapText(text, getFontString(textElement), container
|
|
154
|
+
? getBoundTextMaxWidth(container, textElement)
|
|
155
|
+
: textElement.width);
|
|
147
156
|
}
|
|
148
157
|
const dimensions = getAdjustedDimensions(textElement, elementsMap, text);
|
|
149
158
|
return { text, ...dimensions };
|
|
150
159
|
};
|
|
151
|
-
export const updateTextElement = (textElement, container, elementsMap, { text, isDeleted, originalText, }) => {
|
|
152
|
-
return newElementWith(textElement, {
|
|
153
|
-
originalText,
|
|
154
|
-
isDeleted: isDeleted ?? textElement.isDeleted,
|
|
155
|
-
...refreshTextDimensions(textElement, container, elementsMap, originalText),
|
|
156
|
-
});
|
|
157
|
-
};
|
|
158
160
|
export const newFreeDrawElement = (opts) => {
|
|
159
161
|
return {
|
|
160
162
|
..._newElementBase(opts.type, opts),
|
|
@@ -7,7 +7,7 @@ import { mutateElement } from "./mutateElement";
|
|
|
7
7
|
import { getFontString } from "../utils";
|
|
8
8
|
import { updateBoundElements } from "./binding";
|
|
9
9
|
import Scene from "../scene/Scene";
|
|
10
|
-
import { getApproxMinLineWidth, getBoundTextElement, getBoundTextElementId, getContainerElement, handleBindTextResize, getBoundTextMaxWidth, getApproxMinLineHeight, } from "./textElement";
|
|
10
|
+
import { getApproxMinLineWidth, getBoundTextElement, getBoundTextElementId, getContainerElement, handleBindTextResize, getBoundTextMaxWidth, getApproxMinLineHeight, wrapText, measureText, getMinTextElementWidth, } from "./textElement";
|
|
11
11
|
import { LinearElementEditor } from "./linearElementEditor";
|
|
12
12
|
import { isInGroup } from "../groups";
|
|
13
13
|
export const normalizeAngle = (angle) => {
|
|
@@ -27,12 +27,8 @@ export const transformElements = (originalElements, transformHandleType, selecte
|
|
|
27
27
|
rotateSingleElement(element, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle);
|
|
28
28
|
updateBoundElements(element, elementsMap);
|
|
29
29
|
}
|
|
30
|
-
else if (isTextElement(element) &&
|
|
31
|
-
(transformHandleType
|
|
32
|
-
transformHandleType === "ne" ||
|
|
33
|
-
transformHandleType === "sw" ||
|
|
34
|
-
transformHandleType === "se")) {
|
|
35
|
-
resizeSingleTextElement(element, elementsMap, transformHandleType, shouldResizeFromCenter, pointerX, pointerY);
|
|
30
|
+
else if (isTextElement(element) && transformHandleType) {
|
|
31
|
+
resizeSingleTextElement(originalElements, element, elementsMap, transformHandleType, shouldResizeFromCenter, pointerX, pointerY);
|
|
36
32
|
updateBoundElements(element, elementsMap);
|
|
37
33
|
}
|
|
38
34
|
else if (transformHandleType) {
|
|
@@ -100,23 +96,25 @@ const measureFontSizeFromWidth = (element, elementsMap, nextWidth) => {
|
|
|
100
96
|
size: nextFontSize,
|
|
101
97
|
};
|
|
102
98
|
};
|
|
103
|
-
const resizeSingleTextElement = (element, elementsMap, transformHandleType, shouldResizeFromCenter, pointerX, pointerY) => {
|
|
99
|
+
const resizeSingleTextElement = (originalElements, element, elementsMap, transformHandleType, shouldResizeFromCenter, pointerX, pointerY) => {
|
|
104
100
|
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(element, elementsMap);
|
|
105
101
|
// rotation pointer with reverse angle
|
|
106
102
|
const [rotatedX, rotatedY] = rotate(pointerX, pointerY, cx, cy, -element.angle);
|
|
107
103
|
let scaleX = 0;
|
|
108
104
|
let scaleY = 0;
|
|
109
|
-
if (transformHandleType
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
if (transformHandleType !== "e" && transformHandleType !== "w") {
|
|
106
|
+
if (transformHandleType.includes("e")) {
|
|
107
|
+
scaleX = (rotatedX - x1) / (x2 - x1);
|
|
108
|
+
}
|
|
109
|
+
if (transformHandleType.includes("w")) {
|
|
110
|
+
scaleX = (x2 - rotatedX) / (x2 - x1);
|
|
111
|
+
}
|
|
112
|
+
if (transformHandleType.includes("n")) {
|
|
113
|
+
scaleY = (y2 - rotatedY) / (y2 - y1);
|
|
114
|
+
}
|
|
115
|
+
if (transformHandleType.includes("s")) {
|
|
116
|
+
scaleY = (rotatedY - y1) / (y2 - y1);
|
|
117
|
+
}
|
|
120
118
|
}
|
|
121
119
|
const scale = Math.max(scaleX, scaleY);
|
|
122
120
|
if (scale > 0) {
|
|
@@ -171,6 +169,60 @@ const resizeSingleTextElement = (element, elementsMap, transformHandleType, shou
|
|
|
171
169
|
y: nextY,
|
|
172
170
|
});
|
|
173
171
|
}
|
|
172
|
+
if (transformHandleType === "e" || transformHandleType === "w") {
|
|
173
|
+
const stateAtResizeStart = originalElements.get(element.id);
|
|
174
|
+
const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords(stateAtResizeStart, stateAtResizeStart.width, stateAtResizeStart.height, true);
|
|
175
|
+
const startTopLeft = [x1, y1];
|
|
176
|
+
const startBottomRight = [x2, y2];
|
|
177
|
+
const startCenter = centerPoint(startTopLeft, startBottomRight);
|
|
178
|
+
const rotatedPointer = rotatePoint([pointerX, pointerY], startCenter, -stateAtResizeStart.angle);
|
|
179
|
+
const [esx1, , esx2] = getResizedElementAbsoluteCoords(element, element.width, element.height, true);
|
|
180
|
+
const boundsCurrentWidth = esx2 - esx1;
|
|
181
|
+
const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
|
|
182
|
+
const minWidth = getMinTextElementWidth(getFontString({
|
|
183
|
+
fontSize: element.fontSize,
|
|
184
|
+
fontFamily: element.fontFamily,
|
|
185
|
+
}), element.lineHeight);
|
|
186
|
+
let scaleX = atStartBoundsWidth / boundsCurrentWidth;
|
|
187
|
+
if (transformHandleType.includes("e")) {
|
|
188
|
+
scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
|
|
189
|
+
}
|
|
190
|
+
if (transformHandleType.includes("w")) {
|
|
191
|
+
scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
|
|
192
|
+
}
|
|
193
|
+
const newWidth = element.width * scaleX < minWidth ? minWidth : element.width * scaleX;
|
|
194
|
+
const text = wrapText(element.originalText, getFontString(element), Math.abs(newWidth));
|
|
195
|
+
const metrics = measureText(text, getFontString(element), element.lineHeight);
|
|
196
|
+
const eleNewHeight = metrics.height;
|
|
197
|
+
const [newBoundsX1, newBoundsY1, newBoundsX2, newBoundsY2] = getResizedElementAbsoluteCoords(stateAtResizeStart, newWidth, eleNewHeight, true);
|
|
198
|
+
const newBoundsWidth = newBoundsX2 - newBoundsX1;
|
|
199
|
+
const newBoundsHeight = newBoundsY2 - newBoundsY1;
|
|
200
|
+
let newTopLeft = [...startTopLeft];
|
|
201
|
+
if (["n", "w", "nw"].includes(transformHandleType)) {
|
|
202
|
+
newTopLeft = [
|
|
203
|
+
startBottomRight[0] - Math.abs(newBoundsWidth),
|
|
204
|
+
startTopLeft[1],
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
// adjust topLeft to new rotation point
|
|
208
|
+
const angle = stateAtResizeStart.angle;
|
|
209
|
+
const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle);
|
|
210
|
+
const newCenter = [
|
|
211
|
+
newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
|
|
212
|
+
newTopLeft[1] + Math.abs(newBoundsHeight) / 2,
|
|
213
|
+
];
|
|
214
|
+
const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle);
|
|
215
|
+
newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle);
|
|
216
|
+
const resizedElement = {
|
|
217
|
+
width: Math.abs(newWidth),
|
|
218
|
+
height: Math.abs(metrics.height),
|
|
219
|
+
x: newTopLeft[0],
|
|
220
|
+
y: newTopLeft[1],
|
|
221
|
+
text,
|
|
222
|
+
autoResize: false,
|
|
223
|
+
};
|
|
224
|
+
mutateElement(element, resizedElement);
|
|
225
|
+
}
|
|
174
226
|
};
|
|
175
227
|
export const resizeSingleElement = (originalElements, shouldMaintainAspectRatio, element, elementsMap, transformHandleDirection, shouldResizeFromCenter, pointerX, pointerY) => {
|
|
176
228
|
const stateAtResizeStart = originalElements.get(element.id);
|
|
@@ -541,7 +593,7 @@ export const resizeMultipleElements = (originalElements, selectedElements, eleme
|
|
|
541
593
|
handleBindTextResize(element, elementsMap, transformHandleType, true);
|
|
542
594
|
}
|
|
543
595
|
}
|
|
544
|
-
Scene.getScene(elementsAndUpdates[0].element)?.
|
|
596
|
+
Scene.getScene(elementsAndUpdates[0].element)?.triggerUpdate();
|
|
545
597
|
};
|
|
546
598
|
const rotateMultipleElements = (originalElements, elements, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle, centerX, centerY) => {
|
|
547
599
|
let centerAngle = (5 * Math.PI) / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
|
|
@@ -574,7 +626,7 @@ const rotateMultipleElements = (originalElements, elements, elementsMap, pointer
|
|
|
574
626
|
}, false);
|
|
575
627
|
}
|
|
576
628
|
});
|
|
577
|
-
Scene.getScene(elements[0])?.
|
|
629
|
+
Scene.getScene(elements[0])?.triggerUpdate();
|
|
578
630
|
};
|
|
579
631
|
export const getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x, y) => {
|
|
580
632
|
const [x1, y1, x2, y2] = selectedElements.length === 1
|
|
@@ -28,10 +28,8 @@ export const resizeTest = (element, elementsMap, appState, x, y, zoom, pointerTy
|
|
|
28
28
|
}
|
|
29
29
|
if (canResizeFromSides(device)) {
|
|
30
30
|
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(element, elementsMap);
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
if (element.type !== "text" &&
|
|
34
|
-
!(isLinearElement(element) && element.points.length <= 2)) {
|
|
31
|
+
// do not resize from the sides for linear elements with only two points
|
|
32
|
+
if (!(isLinearElement(element) && element.points.length <= 2)) {
|
|
35
33
|
const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
|
|
36
34
|
const sides = getSelectionBorders([x1 - SPACING, y1 - SPACING], [x2 + SPACING, y2 + SPACING], [cx, cy], angleToDegrees(element.angle));
|
|
37
35
|
for (const [dir, side] of Object.entries(sides)) {
|
|
@@ -87,4 +87,5 @@ export declare const FONT_METRICS: Record<number, {
|
|
|
87
87
|
export declare const getDefaultLineHeight: (fontFamily: FontFamilyValues) => number & {
|
|
88
88
|
_brand: "unitlessLineHeight";
|
|
89
89
|
};
|
|
90
|
+
export declare const getMinTextElementWidth: (font: FontString, lineHeight: ExcalidrawTextElement["lineHeight"]) => number;
|
|
90
91
|
export {};
|
|
@@ -24,12 +24,17 @@ export const redrawTextBoundingBox = (textElement, container, elementsMap, infor
|
|
|
24
24
|
angle: container?.angle ?? textElement.angle,
|
|
25
25
|
};
|
|
26
26
|
boundTextUpdates.text = textElement.text;
|
|
27
|
-
if (container) {
|
|
28
|
-
maxWidth =
|
|
27
|
+
if (container || !textElement.autoResize) {
|
|
28
|
+
maxWidth = container
|
|
29
|
+
? getBoundTextMaxWidth(container, textElement)
|
|
30
|
+
: textElement.width;
|
|
29
31
|
boundTextUpdates.text = wrapText(textElement.originalText, getFontString(textElement), maxWidth);
|
|
30
32
|
}
|
|
31
33
|
const metrics = measureText(boundTextUpdates.text, getFontString(textElement), textElement.lineHeight);
|
|
32
|
-
|
|
34
|
+
// Note: only update width for unwrapped text and bound texts (which always have autoResize set to true)
|
|
35
|
+
if (textElement.autoResize) {
|
|
36
|
+
boundTextUpdates.width = metrics.width;
|
|
37
|
+
}
|
|
33
38
|
boundTextUpdates.height = metrics.height;
|
|
34
39
|
if (container) {
|
|
35
40
|
const maxContainerHeight = getBoundTextMaxHeight(container, textElement);
|
|
@@ -661,3 +666,6 @@ export const getDefaultLineHeight = (fontFamily) => {
|
|
|
661
666
|
}
|
|
662
667
|
return DEFAULT_LINE_HEIGHT[DEFAULT_FONT_FAMILY];
|
|
663
668
|
};
|
|
669
|
+
export const getMinTextElementWidth = (font, lineHeight) => {
|
|
670
|
+
return measureText("", font, lineHeight).width + BOUND_TEXT_PADDING * 2;
|
|
671
|
+
};
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import type { ExcalidrawElement, ExcalidrawTextElement } from "./types";
|
|
2
2
|
import type App from "../components/App";
|
|
3
|
-
export declare const textWysiwyg: ({ id, onChange, onSubmit, getViewportCoords, element, canvas, excalidrawContainer, app, }: {
|
|
3
|
+
export declare const textWysiwyg: ({ id, onChange, onSubmit, getViewportCoords, element, canvas, excalidrawContainer, app, autoSelect, }: {
|
|
4
4
|
id: ExcalidrawElement["id"];
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* textWysiwyg only deals with `originalText`
|
|
7
|
+
*
|
|
8
|
+
* Note: `text`, which can be wrapped and therefore different from `originalText`,
|
|
9
|
+
* is derived from `originalText`
|
|
10
|
+
*/
|
|
11
|
+
onChange?: ((nextOriginalText: string) => void) | undefined;
|
|
6
12
|
onSubmit: (data: {
|
|
7
|
-
text: string;
|
|
8
13
|
viaKeyboard: boolean;
|
|
9
|
-
|
|
14
|
+
nextOriginalText: string;
|
|
10
15
|
}) => void;
|
|
11
16
|
getViewportCoords: (x: number, y: number) => [number, number];
|
|
12
17
|
element: ExcalidrawTextElement;
|
|
13
18
|
canvas: HTMLCanvasElement;
|
|
14
19
|
excalidrawContainer: HTMLDivElement | null;
|
|
15
20
|
app: App;
|
|
21
|
+
autoSelect?: boolean | undefined;
|
|
16
22
|
}) => void;
|