@excalidraw/excalidraw 0.17.1-d2f67e6 → 0.17.1-e63dd02
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/CHANGELOG.md +4 -0
- package/dist/browser/dev/excalidraw-assets-dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-EM6LVGFW.js → chunk-IT7T3AIK.js} +23 -5
- package/dist/browser/dev/excalidraw-assets-dev/chunk-IT7T3AIK.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-B4UMSLQQ.js → chunk-RNHSD5AR.js} +7451 -2098
- package/dist/browser/dev/excalidraw-assets-dev/chunk-RNHSD5AR.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{dist-6QVAH5JA.js → dist-DNSPZDOZ.js} +31 -19
- package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js → en-XV7OZCPP.js} +6 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js → image-77HZYGLG.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css → image-WDHYGKKP.css} +1 -1
- package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css.map → image-WDHYGKKP.css.map} +2 -2
- package/dist/browser/dev/index.css +449 -114
- package/dist/browser/dev/index.css.map +3 -3
- package/dist/browser/dev/index.js +4143 -5956
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/chunk-OYEADJSR.js +63 -0
- package/dist/browser/prod/excalidraw-assets/{chunk-7DXALCB2.js → chunk-PDYFZJMS.js} +3 -3
- package/dist/browser/prod/excalidraw-assets/dist-NLUQPPQQ.js +7 -0
- package/dist/browser/prod/excalidraw-assets/en-YVAVVILW.js +1 -0
- package/dist/browser/prod/excalidraw-assets/image-X3GFZHNN.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +40 -50
- package/dist/dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/dev/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
- package/dist/dev/index.css +449 -114
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +21626 -18122
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionAddToLibrary.d.ts +9 -3
- package/dist/excalidraw/actions/actionBoundText.d.ts +6 -2
- package/dist/excalidraw/actions/actionCanvas.d.ts +36 -12
- package/dist/excalidraw/actions/actionClipboard.d.ts +22 -7
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +12 -5
- package/dist/excalidraw/actions/actionDeleteSelected.js +24 -5
- package/dist/excalidraw/actions/actionDuplicateSelection.js +1 -2
- package/dist/excalidraw/actions/actionElementLock.d.ts +6 -2
- package/dist/excalidraw/actions/actionExport.d.ts +27 -9
- package/dist/excalidraw/actions/actionFinalize.d.ts +6 -2
- package/dist/excalidraw/actions/actionFinalize.js +2 -2
- package/dist/excalidraw/actions/actionFlip.js +2 -2
- package/dist/excalidraw/actions/actionFrame.d.ts +12 -4
- package/dist/excalidraw/actions/actionGroup.d.ts +6 -2
- package/dist/excalidraw/actions/actionHistory.js +4 -4
- package/dist/excalidraw/actions/actionLinearEditor.d.ts +3 -1
- package/dist/excalidraw/actions/actionLinearEditor.js +3 -2
- package/dist/excalidraw/actions/actionLink.d.ts +3 -1
- package/dist/excalidraw/actions/actionMenu.d.ts +9 -3
- package/dist/excalidraw/actions/actionNavigate.d.ts +6 -2
- package/dist/excalidraw/actions/actionProperties.d.ts +411 -56
- package/dist/excalidraw/actions/actionProperties.js +383 -58
- package/dist/excalidraw/actions/actionSelectAll.d.ts +3 -1
- package/dist/excalidraw/actions/actionStyles.d.ts +3 -1
- package/dist/excalidraw/actions/actionStyles.js +3 -2
- package/dist/excalidraw/actions/actionToggleGridMode.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleStats.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleViewMode.d.ts +3 -1
- package/dist/excalidraw/actions/actionToggleZenMode.d.ts +3 -1
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/analytics.js +9 -7
- package/dist/excalidraw/appState.d.ts +1 -0
- package/dist/excalidraw/appState.js +9 -1
- package/dist/excalidraw/binaryheap.d.ts +12 -0
- package/dist/excalidraw/binaryheap.js +93 -0
- package/dist/excalidraw/change.d.ts +2 -1
- package/dist/excalidraw/change.js +6 -4
- package/dist/excalidraw/charts.js +0 -10
- package/dist/excalidraw/components/Actions.js +7 -5
- package/dist/excalidraw/components/App.d.ts +5 -9
- package/dist/excalidraw/components/App.js +218 -161
- package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
- package/dist/excalidraw/components/ButtonIcon.js +8 -0
- package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
- package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
- package/dist/excalidraw/components/ButtonSeparator.js +7 -0
- package/dist/excalidraw/components/ColorPicker/ColorPicker.js +47 -79
- package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
- package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
- package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
- package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
- package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
- package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
- package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
- package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
- package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
- package/dist/excalidraw/components/HelpDialog.js +1 -1
- package/dist/excalidraw/components/HintViewer.js +6 -3
- package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
- package/dist/excalidraw/components/PropertiesPopover.js +31 -0
- package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
- package/dist/excalidraw/components/QuickSearch.js +8 -0
- package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
- package/dist/excalidraw/components/ScrollableList.js +8 -0
- package/dist/excalidraw/components/Stats/Angle.d.ts +7 -3
- package/dist/excalidraw/components/Stats/Angle.js +39 -31
- package/dist/excalidraw/components/Stats/Dimension.d.ts +6 -3
- package/dist/excalidraw/components/Stats/Dimension.js +51 -49
- package/dist/excalidraw/components/Stats/DragInput.d.ts +15 -6
- package/dist/excalidraw/components/Stats/DragInput.js +59 -26
- package/dist/excalidraw/components/Stats/FontSize.d.ts +8 -4
- package/dist/excalidraw/components/Stats/FontSize.js +39 -36
- package/dist/excalidraw/components/Stats/MultiAngle.d.ts +5 -3
- package/dist/excalidraw/components/Stats/MultiAngle.js +43 -34
- package/dist/excalidraw/components/Stats/MultiDimension.d.ts +5 -3
- package/dist/excalidraw/components/Stats/MultiDimension.js +101 -99
- package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +6 -3
- package/dist/excalidraw/components/Stats/MultiFontSize.js +47 -32
- package/dist/excalidraw/components/Stats/MultiPosition.d.ts +3 -1
- package/dist/excalidraw/components/Stats/MultiPosition.js +52 -48
- package/dist/excalidraw/components/Stats/Position.d.ts +5 -1
- package/dist/excalidraw/components/Stats/Position.js +31 -29
- package/dist/excalidraw/components/Stats/index.js +5 -17
- package/dist/excalidraw/components/Stats/utils.d.ts +14 -3
- package/dist/excalidraw/components/Stats/utils.js +48 -9
- package/dist/excalidraw/components/TTDDialog/common.d.ts +2 -2
- package/dist/excalidraw/components/TTDDialog/common.js +3 -7
- package/dist/excalidraw/components/UserList.js +22 -22
- package/dist/excalidraw/components/canvases/StaticCanvas.js +1 -0
- package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
- package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
- package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
- package/dist/excalidraw/components/icons.d.ts +4 -0
- package/dist/excalidraw/components/icons.js +7 -0
- package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
- package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
- package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
- package/dist/excalidraw/constants.d.ts +17 -2
- package/dist/excalidraw/constants.js +21 -4
- package/dist/excalidraw/data/reconcile.js +18 -1
- package/dist/excalidraw/data/restore.js +55 -9
- package/dist/excalidraw/data/transform.js +8 -5
- package/dist/excalidraw/element/binding.d.ts +28 -9
- package/dist/excalidraw/element/binding.js +303 -71
- package/dist/excalidraw/element/collision.d.ts +1 -1
- package/dist/excalidraw/element/collision.js +4 -1
- package/dist/excalidraw/element/dragElements.d.ts +2 -2
- package/dist/excalidraw/element/dragElements.js +13 -3
- package/dist/excalidraw/element/embeddable.d.ts +3 -1
- package/dist/excalidraw/element/heading.d.ts +11 -0
- package/dist/excalidraw/element/heading.js +81 -0
- package/dist/excalidraw/element/index.d.ts +1 -1
- package/dist/excalidraw/element/index.js +1 -1
- package/dist/excalidraw/element/linearElementEditor.d.ts +21 -13
- package/dist/excalidraw/element/linearElementEditor.js +133 -56
- package/dist/excalidraw/element/newElement.d.ts +8 -3
- package/dist/excalidraw/element/newElement.js +15 -2
- package/dist/excalidraw/element/resizeElements.d.ts +4 -3
- package/dist/excalidraw/element/resizeElements.js +47 -23
- package/dist/excalidraw/element/routing.d.ts +13 -0
- package/dist/excalidraw/element/routing.js +641 -0
- package/dist/excalidraw/element/textElement.d.ts +3 -26
- package/dist/excalidraw/element/textElement.js +54 -110
- package/dist/excalidraw/element/textWysiwyg.js +39 -47
- package/dist/excalidraw/element/transformHandles.js +7 -2
- package/dist/excalidraw/element/typeChecks.d.ts +5 -2
- package/dist/excalidraw/element/typeChecks.js +17 -0
- package/dist/excalidraw/element/types.d.ts +12 -1
- package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +21 -0
- package/dist/excalidraw/fonts/ExcalidrawFont.js +112 -0
- package/dist/excalidraw/fonts/index.d.ts +58 -0
- package/dist/excalidraw/fonts/index.js +240 -0
- package/dist/excalidraw/fonts/metadata.d.ts +36 -0
- package/dist/excalidraw/fonts/metadata.js +91 -0
- package/dist/excalidraw/fractionalIndex.d.ts +11 -4
- package/dist/excalidraw/fractionalIndex.js +38 -6
- package/dist/excalidraw/frame.d.ts +1 -1
- package/dist/excalidraw/frame.js +3 -3
- package/dist/excalidraw/history.d.ts +4 -3
- package/dist/excalidraw/history.js +8 -8
- package/dist/excalidraw/index.d.ts +1 -1
- package/dist/excalidraw/index.js +3 -3
- package/dist/excalidraw/locales/en.json +18 -4
- package/dist/excalidraw/math.d.ts +43 -0
- package/dist/excalidraw/math.js +110 -0
- package/dist/excalidraw/mermaid.js +4 -3
- package/dist/excalidraw/renderer/interactiveScene.js +33 -17
- package/dist/excalidraw/renderer/renderElement.d.ts +2 -0
- package/dist/excalidraw/renderer/renderElement.js +74 -54
- package/dist/excalidraw/renderer/staticSvgScene.js +2 -1
- package/dist/excalidraw/scene/Scene.js +9 -3
- package/dist/excalidraw/scene/Shape.js +56 -5
- package/dist/excalidraw/scene/comparisons.d.ts +1 -0
- package/dist/excalidraw/scene/comparisons.js +1 -1
- package/dist/excalidraw/scene/export.d.ts +2 -1
- package/dist/excalidraw/scene/export.js +33 -35
- package/dist/excalidraw/scene/types.d.ts +1 -4
- package/dist/excalidraw/shapes.d.ts +8 -0
- package/dist/excalidraw/shapes.js +57 -0
- package/dist/excalidraw/types.d.ts +8 -3
- package/dist/excalidraw/utils.d.ts +11 -1
- package/dist/excalidraw/utils.js +22 -0
- package/dist/prod/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
- package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
- package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
- package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
- package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
- package/dist/prod/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +49 -53
- package/dist/utils/export.d.ts +2 -1
- package/dist/utils/export.js +2 -1
- package/dist/utils/geometry/geometry.d.ts +2 -1
- package/dist/utils/geometry/geometry.js +5 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/history.ts +9 -2
- package/package.json +2 -2
- package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
- package/dist/browser/dev/excalidraw-assets-dev/chunk-B4UMSLQQ.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-EM6LVGFW.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +0 -7
- package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
- package/dist/browser/prod/excalidraw-assets/chunk-EGOLGOLD.js +0 -55
- package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +0 -7
- package/dist/browser/prod/excalidraw-assets/en-6E7MYLWO.js +0 -1
- package/dist/browser/prod/excalidraw-assets/image-SI7BKULC.js +0 -1
- package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
- package/dist/excalidraw/scene/Fonts.d.ts +0 -19
- package/dist/excalidraw/scene/Fonts.js +0 -66
- package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
- package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
- /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
- /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
- /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
- /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js.map → en-XV7OZCPP.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js.map → image-77HZYGLG.js.map} +0 -0
- /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
- /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
- /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
- /package/dist/browser/prod/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
|
@@ -6,10 +6,11 @@ import rough from "roughjs/bin/rough";
|
|
|
6
6
|
import { getDefaultAppState } from "../appState";
|
|
7
7
|
import { BOUND_TEXT_PADDING, ELEMENT_READY_TO_ERASE_OPACITY, FRAME_STYLE, MIME_TYPES, THEME, } from "../constants";
|
|
8
8
|
import { getStroke } from "perfect-freehand";
|
|
9
|
-
import { getBoundTextElement, getContainerCoords, getContainerElement, getLineHeightInPx, getBoundTextMaxHeight, getBoundTextMaxWidth,
|
|
9
|
+
import { getBoundTextElement, getContainerCoords, getContainerElement, getLineHeightInPx, getBoundTextMaxHeight, getBoundTextMaxWidth, } from "../element/textElement";
|
|
10
10
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
11
11
|
import { getContainingFrame } from "../frame";
|
|
12
12
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
13
|
+
import { getVerticalOffset } from "../fonts";
|
|
13
14
|
// using a stronger invert (100% vs our regular 93%) and saturate
|
|
14
15
|
// as a temp hack to make images in dark theme look closer to original
|
|
15
16
|
// color scheme (it's still not quite there and the colors look slightly
|
|
@@ -24,7 +25,16 @@ const shouldResetImageFilter = (element, renderConfig, appState) => {
|
|
|
24
25
|
!isPendingImageElement(element, renderConfig) &&
|
|
25
26
|
renderConfig.imageCache.get(element.fileId)?.mimeType !== MIME_TYPES.svg);
|
|
26
27
|
};
|
|
27
|
-
const getCanvasPadding = (element) =>
|
|
28
|
+
const getCanvasPadding = (element) => {
|
|
29
|
+
switch (element.type) {
|
|
30
|
+
case "freedraw":
|
|
31
|
+
return element.strokeWidth * 12;
|
|
32
|
+
case "text":
|
|
33
|
+
return element.fontSize / 2;
|
|
34
|
+
default:
|
|
35
|
+
return 20;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
28
38
|
export const getRenderOpacity = (element, containingFrame, elementsPendingErasure) => {
|
|
29
39
|
// multiplying frame opacity with element opacity to combine them
|
|
30
40
|
// (e.g. frame 50% and element 50% opacity should result in 25% opacity)
|
|
@@ -78,7 +88,7 @@ const generateElementCanvas = (element, elementsMap, zoom, renderConfig, appStat
|
|
|
78
88
|
const { width, height, scale } = cappedElementCanvasSize(element, elementsMap, zoom);
|
|
79
89
|
canvas.width = width;
|
|
80
90
|
canvas.height = height;
|
|
81
|
-
let canvasOffsetX =
|
|
91
|
+
let canvasOffsetX = -100;
|
|
82
92
|
let canvasOffsetY = 0;
|
|
83
93
|
if (isLinearElement(element) || isFreeDrawElement(element)) {
|
|
84
94
|
const [x1, y1] = getElementAbsoluteCoords(element, elementsMap);
|
|
@@ -102,6 +112,45 @@ const generateElementCanvas = (element, elementsMap, zoom, renderConfig, appStat
|
|
|
102
112
|
}
|
|
103
113
|
drawElementOnCanvas(element, rc, context, renderConfig, appState);
|
|
104
114
|
context.restore();
|
|
115
|
+
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
116
|
+
const boundTextCanvas = document.createElement("canvas");
|
|
117
|
+
const boundTextCanvasContext = boundTextCanvas.getContext("2d");
|
|
118
|
+
if (isArrowElement(element) && boundTextElement) {
|
|
119
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
120
|
+
// Take max dimensions of arrow canvas so that when canvas is rotated
|
|
121
|
+
// the arrow doesn't get clipped
|
|
122
|
+
const maxDim = Math.max(distance(x1, x2), distance(y1, y2));
|
|
123
|
+
boundTextCanvas.width =
|
|
124
|
+
maxDim * window.devicePixelRatio * scale + padding * scale * 10;
|
|
125
|
+
boundTextCanvas.height =
|
|
126
|
+
maxDim * window.devicePixelRatio * scale + padding * scale * 10;
|
|
127
|
+
boundTextCanvasContext.translate(boundTextCanvas.width / 2, boundTextCanvas.height / 2);
|
|
128
|
+
boundTextCanvasContext.rotate(element.angle);
|
|
129
|
+
boundTextCanvasContext.drawImage(canvas, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
|
|
130
|
+
const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement, elementsMap);
|
|
131
|
+
boundTextCanvasContext.rotate(-element.angle);
|
|
132
|
+
const offsetX = (boundTextCanvas.width - canvas.width) / 2;
|
|
133
|
+
const offsetY = (boundTextCanvas.height - canvas.height) / 2;
|
|
134
|
+
const shiftX = boundTextCanvas.width / 2 -
|
|
135
|
+
(boundTextCx - x1) * window.devicePixelRatio * scale -
|
|
136
|
+
offsetX -
|
|
137
|
+
padding * scale;
|
|
138
|
+
const shiftY = boundTextCanvas.height / 2 -
|
|
139
|
+
(boundTextCy - y1) * window.devicePixelRatio * scale -
|
|
140
|
+
offsetY -
|
|
141
|
+
padding * scale;
|
|
142
|
+
boundTextCanvasContext.translate(-shiftX, -shiftY);
|
|
143
|
+
// Clear the bound text area
|
|
144
|
+
boundTextCanvasContext.clearRect(-(boundTextElement.width / 2 + BOUND_TEXT_PADDING) *
|
|
145
|
+
window.devicePixelRatio *
|
|
146
|
+
scale, -(boundTextElement.height / 2 + BOUND_TEXT_PADDING) *
|
|
147
|
+
window.devicePixelRatio *
|
|
148
|
+
scale, (boundTextElement.width + BOUND_TEXT_PADDING * 2) *
|
|
149
|
+
window.devicePixelRatio *
|
|
150
|
+
scale, (boundTextElement.height + BOUND_TEXT_PADDING * 2) *
|
|
151
|
+
window.devicePixelRatio *
|
|
152
|
+
scale);
|
|
153
|
+
}
|
|
105
154
|
return {
|
|
106
155
|
element,
|
|
107
156
|
canvas,
|
|
@@ -112,6 +161,8 @@ const generateElementCanvas = (element, elementsMap, zoom, renderConfig, appStat
|
|
|
112
161
|
canvasOffsetY,
|
|
113
162
|
boundTextElementVersion: getBoundTextElement(element, elementsMap)?.version || null,
|
|
114
163
|
containingFrameOpacity: getContainingFrame(element, elementsMap)?.opacity || 100,
|
|
164
|
+
boundTextCanvas,
|
|
165
|
+
angle: element.angle,
|
|
115
166
|
};
|
|
116
167
|
};
|
|
117
168
|
export const DEFAULT_LINK_SIZE = 14;
|
|
@@ -224,13 +275,21 @@ const generateElementWithCanvas = (element, elementsMap, renderConfig, appState)
|
|
|
224
275
|
const shouldRegenerateBecauseZoom = prevElementWithCanvas &&
|
|
225
276
|
prevElementWithCanvas.zoomValue !== zoom.value &&
|
|
226
277
|
!appState?.shouldCacheIgnoreZoom;
|
|
227
|
-
const
|
|
278
|
+
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
279
|
+
const boundTextElementVersion = boundTextElement?.version || null;
|
|
228
280
|
const containingFrameOpacity = getContainingFrame(element, elementsMap)?.opacity || 100;
|
|
229
281
|
if (!prevElementWithCanvas ||
|
|
230
282
|
shouldRegenerateBecauseZoom ||
|
|
231
283
|
prevElementWithCanvas.theme !== appState.theme ||
|
|
232
284
|
prevElementWithCanvas.boundTextElementVersion !== boundTextElementVersion ||
|
|
233
|
-
prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity
|
|
285
|
+
prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity ||
|
|
286
|
+
// since we rotate the canvas when copying from cached canvas, we don't
|
|
287
|
+
// regenerate the cached canvas. But we need to in case of labels which are
|
|
288
|
+
// cached alongside the arrow, and we want the labels to remain unrotated
|
|
289
|
+
// with respect to the arrow.
|
|
290
|
+
(isArrowElement(element) &&
|
|
291
|
+
boundTextElement &&
|
|
292
|
+
element.angle !== prevElementWithCanvas.angle)) {
|
|
234
293
|
const elementWithCanvas = generateElementCanvas(element, elementsMap, zoom, renderConfig, appState);
|
|
235
294
|
elementWithCanvasCache.set(element, elementWithCanvas);
|
|
236
295
|
return elementWithCanvas;
|
|
@@ -241,60 +300,21 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
|
|
|
241
300
|
const element = elementWithCanvas.element;
|
|
242
301
|
const padding = getCanvasPadding(element);
|
|
243
302
|
const zoom = elementWithCanvas.scale;
|
|
244
|
-
|
|
245
|
-
// Free draw elements will otherwise "shuffle" as the min x and y change
|
|
246
|
-
if (isFreeDrawElement(element)) {
|
|
247
|
-
x1 = Math.floor(x1);
|
|
248
|
-
x2 = Math.ceil(x2);
|
|
249
|
-
y1 = Math.floor(y1);
|
|
250
|
-
y2 = Math.ceil(y2);
|
|
251
|
-
}
|
|
303
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, allElementsMap);
|
|
252
304
|
const cx = ((x1 + x2) / 2 + appState.scrollX) * window.devicePixelRatio;
|
|
253
305
|
const cy = ((y1 + y2) / 2 + appState.scrollY) * window.devicePixelRatio;
|
|
254
306
|
context.save();
|
|
255
307
|
context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
|
|
256
308
|
const boundTextElement = getBoundTextElement(element, allElementsMap);
|
|
257
309
|
if (isArrowElement(element) && boundTextElement) {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
maxDim * window.devicePixelRatio * zoom +
|
|
265
|
-
padding * elementWithCanvas.scale * 10;
|
|
266
|
-
tempCanvas.height =
|
|
267
|
-
maxDim * window.devicePixelRatio * zoom +
|
|
268
|
-
padding * elementWithCanvas.scale * 10;
|
|
269
|
-
const offsetX = (tempCanvas.width - elementWithCanvas.canvas.width) / 2;
|
|
270
|
-
const offsetY = (tempCanvas.height - elementWithCanvas.canvas.height) / 2;
|
|
271
|
-
tempCanvasContext.translate(tempCanvas.width / 2, tempCanvas.height / 2);
|
|
272
|
-
tempCanvasContext.rotate(element.angle);
|
|
273
|
-
tempCanvasContext.drawImage(elementWithCanvas.canvas, -elementWithCanvas.canvas.width / 2, -elementWithCanvas.canvas.height / 2, elementWithCanvas.canvas.width, elementWithCanvas.canvas.height);
|
|
274
|
-
const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement, allElementsMap);
|
|
275
|
-
tempCanvasContext.rotate(-element.angle);
|
|
276
|
-
// Shift the canvas to the center of the bound text element
|
|
277
|
-
const shiftX = tempCanvas.width / 2 -
|
|
278
|
-
(boundTextCx - x1) * window.devicePixelRatio * zoom -
|
|
279
|
-
offsetX -
|
|
280
|
-
padding * zoom;
|
|
281
|
-
const shiftY = tempCanvas.height / 2 -
|
|
282
|
-
(boundTextCy - y1) * window.devicePixelRatio * zoom -
|
|
283
|
-
offsetY -
|
|
284
|
-
padding * zoom;
|
|
285
|
-
tempCanvasContext.translate(-shiftX, -shiftY);
|
|
286
|
-
// Clear the bound text area
|
|
287
|
-
tempCanvasContext.clearRect(-(boundTextElement.width / 2 + BOUND_TEXT_PADDING) *
|
|
288
|
-
window.devicePixelRatio *
|
|
289
|
-
zoom, -(boundTextElement.height / 2 + BOUND_TEXT_PADDING) *
|
|
290
|
-
window.devicePixelRatio *
|
|
291
|
-
zoom, (boundTextElement.width + BOUND_TEXT_PADDING * 2) *
|
|
292
|
-
window.devicePixelRatio *
|
|
293
|
-
zoom, (boundTextElement.height + BOUND_TEXT_PADDING * 2) *
|
|
294
|
-
window.devicePixelRatio *
|
|
295
|
-
zoom);
|
|
310
|
+
const offsetX = (elementWithCanvas.boundTextCanvas.width -
|
|
311
|
+
elementWithCanvas.canvas.width) /
|
|
312
|
+
2;
|
|
313
|
+
const offsetY = (elementWithCanvas.boundTextCanvas.height -
|
|
314
|
+
elementWithCanvas.canvas.height) /
|
|
315
|
+
2;
|
|
296
316
|
context.translate(cx, cy);
|
|
297
|
-
context.drawImage(
|
|
317
|
+
context.drawImage(elementWithCanvas.boundTextCanvas, (-(x2 - x1) / 2) * window.devicePixelRatio - offsetX / zoom - padding, (-(y2 - y1) / 2) * window.devicePixelRatio - offsetY / zoom - padding, elementWithCanvas.boundTextCanvas.width / zoom, elementWithCanvas.boundTextCanvas.height / zoom);
|
|
298
318
|
}
|
|
299
319
|
else {
|
|
300
320
|
// we translate context to element center so that rotation and scale
|
|
@@ -386,7 +406,7 @@ export const renderElement = (element, elementsMap, allElementsMap, rc, context,
|
|
|
386
406
|
context.restore();
|
|
387
407
|
}
|
|
388
408
|
else {
|
|
389
|
-
const elementWithCanvas = generateElementWithCanvas(element,
|
|
409
|
+
const elementWithCanvas = generateElementWithCanvas(element, allElementsMap, renderConfig, appState);
|
|
390
410
|
drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState, allElementsMap);
|
|
391
411
|
}
|
|
392
412
|
break;
|
|
@@ -470,7 +490,7 @@ export const renderElement = (element, elementsMap, allElementsMap, rc, context,
|
|
|
470
490
|
// canvases)
|
|
471
491
|
}
|
|
472
492
|
else {
|
|
473
|
-
const elementWithCanvas = generateElementWithCanvas(element,
|
|
493
|
+
const elementWithCanvas = generateElementWithCanvas(element, allElementsMap, renderConfig, appState);
|
|
474
494
|
const currentImageSmoothingStatus = context.imageSmoothingEnabled;
|
|
475
495
|
if (
|
|
476
496
|
// do not disable smoothing during zoom as blurry shapes look better
|
|
@@ -3,13 +3,14 @@ import { normalizeLink, toValidURL } from "../data/url";
|
|
|
3
3
|
import { getElementAbsoluteCoords } from "../element";
|
|
4
4
|
import { createPlaceholderEmbeddableLabel, getEmbedLink, } from "../element/embeddable";
|
|
5
5
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
6
|
-
import { getBoundTextElement, getContainerElement, getLineHeightInPx,
|
|
6
|
+
import { getBoundTextElement, getContainerElement, getLineHeightInPx, } from "../element/textElement";
|
|
7
7
|
import { isArrowElement, isIframeLikeElement, isInitializedImageElement, isTextElement, } from "../element/typeChecks";
|
|
8
8
|
import { getContainingFrame } from "../frame";
|
|
9
9
|
import { getCornerRadius, isPathALoop } from "../math";
|
|
10
10
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
11
11
|
import { getFontFamilyString, isRTL, isTestEnv } from "../utils";
|
|
12
12
|
import { getFreeDrawSvgPath, IMAGE_INVERT_FILTER } from "./renderElement";
|
|
13
|
+
import { getVerticalOffset } from "../fonts";
|
|
13
14
|
const roughSVGDrawWithPrecision = (rsvg, drawable, precision) => {
|
|
14
15
|
if (typeof precision === "undefined") {
|
|
15
16
|
return rsvg.draw(drawable);
|
|
@@ -170,9 +170,15 @@ class Scene {
|
|
|
170
170
|
? nextElements
|
|
171
171
|
: Array.from(nextElements.values());
|
|
172
172
|
const nextFrameLikes = [];
|
|
173
|
-
if (import.meta.env.DEV ||
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
if (import.meta.env.DEV ||
|
|
174
|
+
import.meta.env.MODE === ENV.TEST ||
|
|
175
|
+
window?.DEBUG_FRACTIONAL_INDICES) {
|
|
176
|
+
validateFractionalIndices(_nextElements, {
|
|
177
|
+
// validate everything
|
|
178
|
+
includeBoundTextValidation: true,
|
|
179
|
+
// throw only in dev & test, to remain functional on `DEBUG_FRACTIONAL_INDICES`
|
|
180
|
+
shouldThrow: import.meta.env.DEV || import.meta.env.MODE === ENV.TEST,
|
|
181
|
+
});
|
|
176
182
|
}
|
|
177
183
|
this.elements = syncInvalidIndices(_nextElements);
|
|
178
184
|
this.elementsMap.clear();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getDiamondPoints, getArrowheadPoints } from "../element";
|
|
2
|
-
import { isPathALoop, getCornerRadius } from "../math";
|
|
2
|
+
import { isPathALoop, getCornerRadius, distanceSq2d } from "../math";
|
|
3
3
|
import { generateFreeDrawShape } from "../renderer/renderElement";
|
|
4
4
|
import { isTransparent, assertNever } from "../utils";
|
|
5
5
|
import { simplify } from "points-on-curve";
|
|
6
6
|
import { ROUGHNESS } from "../constants";
|
|
7
|
-
import { isEmbeddableElement, isIframeElement, isIframeLikeElement, isLinearElement, } from "../element/typeChecks";
|
|
7
|
+
import { isElbowArrow, isEmbeddableElement, isIframeElement, isIframeLikeElement, isLinearElement, } from "../element/typeChecks";
|
|
8
8
|
import { canChangeRoundness } from "./comparisons";
|
|
9
9
|
const getDashArrayDashed = (strokeWidth) => [8, 8 + strokeWidth];
|
|
10
10
|
const getDashArrayDotted = (strokeWidth) => [1.5, 6 + strokeWidth];
|
|
@@ -261,9 +261,14 @@ export const _generateElementShape = (element, generator, { isExporting, canvasB
|
|
|
261
261
|
// points array can be empty in the beginning, so it is important to add
|
|
262
262
|
// initial position to it
|
|
263
263
|
const points = element.points.length ? element.points : [[0, 0]];
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
if (isElbowArrow(element)) {
|
|
265
|
+
shape = [
|
|
266
|
+
generator.path(generateElbowArrowShape(points, 16), generateRoughOptions(element, true)),
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
else if (!element.roundness) {
|
|
270
|
+
// curve is always the first element
|
|
271
|
+
// this simplifies finding the curve for an element
|
|
267
272
|
if (options.fill) {
|
|
268
273
|
shape = [generator.polygon(points, options)];
|
|
269
274
|
}
|
|
@@ -322,3 +327,49 @@ export const _generateElementShape = (element, generator, { isExporting, canvasB
|
|
|
322
327
|
}
|
|
323
328
|
}
|
|
324
329
|
};
|
|
330
|
+
const generateElbowArrowShape = (points, radius) => {
|
|
331
|
+
const subpoints = [];
|
|
332
|
+
for (let i = 1; i < points.length - 1; i += 1) {
|
|
333
|
+
const prev = points[i - 1];
|
|
334
|
+
const next = points[i + 1];
|
|
335
|
+
const corner = Math.min(radius, Math.sqrt(distanceSq2d(points[i], next)) / 2, Math.sqrt(distanceSq2d(points[i], prev)) / 2);
|
|
336
|
+
if (prev[0] < points[i][0] && prev[1] === points[i][1]) {
|
|
337
|
+
// LEFT
|
|
338
|
+
subpoints.push([points[i][0] - corner, points[i][1]]);
|
|
339
|
+
}
|
|
340
|
+
else if (prev[0] === points[i][0] && prev[1] < points[i][1]) {
|
|
341
|
+
// UP
|
|
342
|
+
subpoints.push([points[i][0], points[i][1] - corner]);
|
|
343
|
+
}
|
|
344
|
+
else if (prev[0] > points[i][0] && prev[1] === points[i][1]) {
|
|
345
|
+
// RIGHT
|
|
346
|
+
subpoints.push([points[i][0] + corner, points[i][1]]);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
subpoints.push([points[i][0], points[i][1] + corner]);
|
|
350
|
+
}
|
|
351
|
+
subpoints.push(points[i]);
|
|
352
|
+
if (next[0] < points[i][0] && next[1] === points[i][1]) {
|
|
353
|
+
// LEFT
|
|
354
|
+
subpoints.push([points[i][0] - corner, points[i][1]]);
|
|
355
|
+
}
|
|
356
|
+
else if (next[0] === points[i][0] && next[1] < points[i][1]) {
|
|
357
|
+
// UP
|
|
358
|
+
subpoints.push([points[i][0], points[i][1] - corner]);
|
|
359
|
+
}
|
|
360
|
+
else if (next[0] > points[i][0] && next[1] === points[i][1]) {
|
|
361
|
+
// RIGHT
|
|
362
|
+
subpoints.push([points[i][0] + corner, points[i][1]]);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
subpoints.push([points[i][0], points[i][1] + corner]);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const d = [`M ${points[0][0]} ${points[0][1]}`];
|
|
369
|
+
for (let i = 0; i < subpoints.length; i += 3) {
|
|
370
|
+
d.push(`L ${subpoints[i][0]} ${subpoints[i][1]}`);
|
|
371
|
+
d.push(`Q ${subpoints[i + 1][0]} ${subpoints[i + 1][1]}, ${subpoints[i + 2][0]} ${subpoints[i + 2][1]}`);
|
|
372
|
+
}
|
|
373
|
+
d.push(`L ${points[points.length - 1][0]} ${points[points.length - 1][1]}`);
|
|
374
|
+
return d.join(" ");
|
|
375
|
+
};
|
|
@@ -5,6 +5,7 @@ export declare const hasStrokeColor: (type: ElementOrToolType) => boolean;
|
|
|
5
5
|
export declare const hasStrokeWidth: (type: ElementOrToolType) => boolean;
|
|
6
6
|
export declare const hasStrokeStyle: (type: ElementOrToolType) => boolean;
|
|
7
7
|
export declare const canChangeRoundness: (type: ElementOrToolType) => boolean;
|
|
8
|
+
export declare const toolIsArrow: (type: ElementOrToolType) => boolean;
|
|
8
9
|
export declare const canHaveArrowheads: (type: ElementOrToolType) => boolean;
|
|
9
10
|
export declare const getElementAtPosition: (elements: readonly NonDeletedExcalidrawElement[], isAtPositionFn: (element: NonDeletedExcalidrawElement) => boolean) => NonDeletedExcalidrawElement | null;
|
|
10
11
|
export declare const getElementsAtPosition: (elements: readonly NonDeletedExcalidrawElement[], isAtPositionFn: (element: NonDeletedExcalidrawElement) => boolean) => NonDeletedExcalidrawElement[];
|
|
@@ -25,10 +25,10 @@ export const hasStrokeStyle = (type) => type === "rectangle" ||
|
|
|
25
25
|
export const canChangeRoundness = (type) => type === "rectangle" ||
|
|
26
26
|
type === "iframe" ||
|
|
27
27
|
type === "embeddable" ||
|
|
28
|
-
type === "arrow" ||
|
|
29
28
|
type === "line" ||
|
|
30
29
|
type === "diamond" ||
|
|
31
30
|
type === "image";
|
|
31
|
+
export const toolIsArrow = (type) => type === "arrow";
|
|
32
32
|
export const canHaveArrowheads = (type) => type === "arrow";
|
|
33
33
|
export const getElementAtPosition = (elements, isAtPositionFn) => {
|
|
34
34
|
let hitElement = null;
|
|
@@ -8,7 +8,7 @@ export declare const exportToCanvas: (elements: readonly NonDeletedExcalidrawEle
|
|
|
8
8
|
}, createCanvas?: (width: number, height: number) => {
|
|
9
9
|
canvas: HTMLCanvasElement;
|
|
10
10
|
scale: number;
|
|
11
|
-
}) => Promise<HTMLCanvasElement>;
|
|
11
|
+
}, loadFonts?: () => Promise<void>) => Promise<HTMLCanvasElement>;
|
|
12
12
|
export declare const exportToSvg: (elements: readonly NonDeletedExcalidrawElement[], appState: {
|
|
13
13
|
exportBackground: boolean;
|
|
14
14
|
exportPadding?: number;
|
|
@@ -23,5 +23,6 @@ export declare const exportToSvg: (elements: readonly NonDeletedExcalidrawElemen
|
|
|
23
23
|
*/
|
|
24
24
|
renderEmbeddables?: boolean;
|
|
25
25
|
exportingFrame?: ExcalidrawFrameLikeElement | null;
|
|
26
|
+
skipInliningFonts?: true;
|
|
26
27
|
}) => Promise<SVGSVGElement>;
|
|
27
28
|
export declare const getExportSize: (elements: readonly NonDeletedExcalidrawElement[], exportPadding: number, scale: number) => [number, number];
|
|
@@ -2,16 +2,17 @@ import rough from "roughjs/bin/rough";
|
|
|
2
2
|
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
|
|
3
3
|
import { renderSceneToSvg } from "../renderer/staticSvgScene";
|
|
4
4
|
import { arrayToMap, distance, getFontString, toBrandedType } from "../utils";
|
|
5
|
-
import { DEFAULT_EXPORT_PADDING,
|
|
5
|
+
import { DEFAULT_EXPORT_PADDING, FRAME_STYLE, FONT_FAMILY, SVG_NS, THEME, THEME_FILTER, } from "../constants";
|
|
6
6
|
import { getDefaultAppState } from "../appState";
|
|
7
7
|
import { serializeAsJSON } from "../data/json";
|
|
8
8
|
import { getInitializedImageElements, updateImageCache, } from "../element/image";
|
|
9
9
|
import { getElementsOverlappingFrame, getFrameLikeElements, getFrameLikeTitle, getRootElements, } from "../frame";
|
|
10
10
|
import { newTextElement } from "../element";
|
|
11
11
|
import { newElementWith } from "../element/mutateElement";
|
|
12
|
-
import {
|
|
12
|
+
import { isFrameLikeElement, isTextElement } from "../element/typeChecks";
|
|
13
13
|
import { syncInvalidIndices } from "../fractionalIndex";
|
|
14
14
|
import { renderStaticScene } from "../renderer/staticScene";
|
|
15
|
+
import { Fonts } from "../fonts";
|
|
15
16
|
const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
|
16
17
|
const truncateText = (element, maxWidth) => {
|
|
17
18
|
if (element.width <= maxWidth) {
|
|
@@ -48,26 +49,18 @@ const truncateText = (element, maxWidth) => {
|
|
|
48
49
|
*/
|
|
49
50
|
const addFrameLabelsAsTextElements = (elements, opts) => {
|
|
50
51
|
const nextElements = [];
|
|
51
|
-
let frameIndex = 0;
|
|
52
|
-
let magicFrameIndex = 0;
|
|
53
52
|
for (const element of elements) {
|
|
54
53
|
if (isFrameLikeElement(element)) {
|
|
55
|
-
if (isFrameElement(element)) {
|
|
56
|
-
frameIndex++;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
magicFrameIndex++;
|
|
60
|
-
}
|
|
61
54
|
let textElement = newTextElement({
|
|
62
55
|
x: element.x,
|
|
63
56
|
y: element.y - FRAME_STYLE.nameOffsetY,
|
|
64
|
-
fontFamily: FONT_FAMILY.
|
|
57
|
+
fontFamily: FONT_FAMILY.Helvetica,
|
|
65
58
|
fontSize: FRAME_STYLE.nameFontSize,
|
|
66
59
|
lineHeight: FRAME_STYLE.nameLineHeight,
|
|
67
60
|
strokeColor: opts.exportWithDarkMode
|
|
68
61
|
? FRAME_STYLE.nameColorDarkTheme
|
|
69
62
|
: FRAME_STYLE.nameColorLightTheme,
|
|
70
|
-
text: getFrameLikeTitle(element
|
|
63
|
+
text: getFrameLikeTitle(element),
|
|
71
64
|
});
|
|
72
65
|
textElement.y -= textElement.height;
|
|
73
66
|
textElement = truncateText(textElement, element.width);
|
|
@@ -106,7 +99,11 @@ export const exportToCanvas = async (elements, appState, files, { exportBackgrou
|
|
|
106
99
|
canvas.width = width * appState.exportScale;
|
|
107
100
|
canvas.height = height * appState.exportScale;
|
|
108
101
|
return { canvas, scale: appState.exportScale };
|
|
102
|
+
}, loadFonts = async () => {
|
|
103
|
+
await Fonts.loadFontsForElements(elements);
|
|
109
104
|
}) => {
|
|
105
|
+
// load font faces before continuing, by default leverages browsers' [FontFace API](https://developer.mozilla.org/en-US/docs/Web/API/FontFace)
|
|
106
|
+
await loadFonts();
|
|
110
107
|
const frameRendering = getFrameRenderingConfig(exportingFrame ?? null, appState.frameRendering ?? null);
|
|
111
108
|
const elementsForRender = prepareElementsForRender({
|
|
112
109
|
elements,
|
|
@@ -195,17 +192,6 @@ export const exportToSvg = async (elements, appState, files, opts) => {
|
|
|
195
192
|
if (exportWithDarkMode) {
|
|
196
193
|
svgRoot.setAttribute("filter", THEME_FILTER);
|
|
197
194
|
}
|
|
198
|
-
let assetPath = "https://excalidraw.com/";
|
|
199
|
-
// Asset path needs to be determined only when using package
|
|
200
|
-
if (import.meta.env.VITE_IS_EXCALIDRAW_NPM_PACKAGE) {
|
|
201
|
-
assetPath =
|
|
202
|
-
window.EXCALIDRAW_ASSET_PATH ||
|
|
203
|
-
`https://unpkg.com/${import.meta.env.VITE_PKG_NAME}@${import.meta.env.VITE_PKG_VERSION}`;
|
|
204
|
-
if (assetPath?.startsWith("/")) {
|
|
205
|
-
assetPath = assetPath.replace("/", `${window.location.origin}/`);
|
|
206
|
-
}
|
|
207
|
-
assetPath = `${assetPath}/dist/excalidraw-assets/`;
|
|
208
|
-
}
|
|
209
195
|
const offsetX = -minX + exportPadding;
|
|
210
196
|
const offsetY = -minY + exportPadding;
|
|
211
197
|
const frameElements = getFrameLikeElements(elements);
|
|
@@ -223,23 +209,35 @@ export const exportToSvg = async (elements, appState, files, opts) => {
|
|
|
223
209
|
</rect>
|
|
224
210
|
</clipPath>`;
|
|
225
211
|
}
|
|
212
|
+
const fontFamilies = elements.reduce((acc, element) => {
|
|
213
|
+
if (isTextElement(element)) {
|
|
214
|
+
acc.add(element.fontFamily);
|
|
215
|
+
}
|
|
216
|
+
return acc;
|
|
217
|
+
}, new Set());
|
|
218
|
+
const fontFaces = opts?.skipInliningFonts
|
|
219
|
+
? []
|
|
220
|
+
: await Promise.all(Array.from(fontFamilies).map(async (x) => {
|
|
221
|
+
const { fonts, metadata } = Fonts.registered.get(x) ?? {};
|
|
222
|
+
if (!Array.isArray(fonts)) {
|
|
223
|
+
console.error(`Couldn't find registered fonts for font-family "${x}"`, Fonts.registered);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (metadata?.local) {
|
|
227
|
+
// don't inline local fonts
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
return Promise.all(fonts.map(async (font) => `@font-face {
|
|
231
|
+
font-family: ${font.fontFace.family};
|
|
232
|
+
src: url(${await font.getContent()});
|
|
233
|
+
}`));
|
|
234
|
+
}));
|
|
226
235
|
svgRoot.innerHTML = `
|
|
227
236
|
${SVG_EXPORT_TAG}
|
|
228
237
|
${metadata}
|
|
229
238
|
<defs>
|
|
230
239
|
<style class="style-fonts">
|
|
231
|
-
|
|
232
|
-
font-family: "Virgil";
|
|
233
|
-
src: url("${assetPath}Virgil.woff2");
|
|
234
|
-
}
|
|
235
|
-
@font-face {
|
|
236
|
-
font-family: "Cascadia";
|
|
237
|
-
src: url("${assetPath}Cascadia.woff2");
|
|
238
|
-
}
|
|
239
|
-
@font-face {
|
|
240
|
-
font-family: "Assistant";
|
|
241
|
-
src: url("${assetPath}Assistant-Regular.woff2");
|
|
242
|
-
}
|
|
240
|
+
${fontFaces.flat().filter(Boolean).join("\n")}
|
|
243
241
|
</style>
|
|
244
242
|
${exportingFrameClipPath}
|
|
245
243
|
</defs>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RoughCanvas } from "roughjs/bin/canvas";
|
|
2
2
|
import type { Drawable } from "roughjs/bin/core";
|
|
3
|
-
import type { ExcalidrawElement,
|
|
3
|
+
import type { ExcalidrawElement, NonDeletedElementsMap, NonDeletedExcalidrawElement, NonDeletedSceneElementsMap } from "../element/types";
|
|
4
4
|
import type { AppClassProperties, AppState, EmbedsValidationStatus, ElementsPendingErasure, InteractiveCanvasAppState, StaticCanvasAppState, SocketId, UserIdleState, Device } from "../types";
|
|
5
5
|
import type { MakeBrand } from "../utility-types";
|
|
6
6
|
export type RenderableElementsMap = NonDeletedElementsMap & MakeBrand<"RenderableElementsMap">;
|
|
@@ -67,9 +67,6 @@ export type SceneScroll = {
|
|
|
67
67
|
scrollX: number;
|
|
68
68
|
scrollY: number;
|
|
69
69
|
};
|
|
70
|
-
export interface Scene {
|
|
71
|
-
elements: ExcalidrawTextElement[];
|
|
72
|
-
}
|
|
73
70
|
export type ExportType = "png" | "clipboard" | "clipboard-svg" | "backend" | "svg";
|
|
74
71
|
export type ScrollBars = {
|
|
75
72
|
horizontal: {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
+
import { type GeometricShape } from "../utils/geometry/shape";
|
|
3
|
+
import type { ElementsMap, ExcalidrawElement } from "./element/types";
|
|
2
4
|
export declare const SHAPES: readonly [{
|
|
3
5
|
readonly icon: JSX.Element;
|
|
4
6
|
readonly value: "selection";
|
|
@@ -61,3 +63,9 @@ export declare const SHAPES: readonly [{
|
|
|
61
63
|
readonly fillable: false;
|
|
62
64
|
}];
|
|
63
65
|
export declare const findShapeByKey: (key: string) => "text" | "image" | "selection" | "rectangle" | "diamond" | "ellipse" | "arrow" | "line" | "freedraw" | "eraser" | null;
|
|
66
|
+
/**
|
|
67
|
+
* get the pure geometric shape of an excalidraw element
|
|
68
|
+
* which is then used for hit detection
|
|
69
|
+
*/
|
|
70
|
+
export declare const getElementShape: (element: ExcalidrawElement, elementsMap: ElementsMap) => GeometricShape;
|
|
71
|
+
export declare const getBoundTextShape: (element: ExcalidrawElement, elementsMap: ElementsMap) => GeometricShape | null;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { getClosedCurveShape, getCurveShape, getEllipseShape, getFreedrawShape, getPolygonShape, } from "../utils/geometry/shape";
|
|
1
2
|
import { ArrowIcon, DiamondIcon, EllipseIcon, EraserIcon, FreedrawIcon, ImageIcon, LineIcon, RectangleIcon, SelectionIcon, TextIcon, } from "./components/icons";
|
|
3
|
+
import { getElementAbsoluteCoords } from "./element";
|
|
4
|
+
import { shouldTestInside } from "./element/collision";
|
|
5
|
+
import { LinearElementEditor } from "./element/linearElementEditor";
|
|
6
|
+
import { getBoundTextElement } from "./element/textElement";
|
|
2
7
|
import { KEYS } from "./keys";
|
|
8
|
+
import { ShapeCache } from "./scene/ShapeCache";
|
|
3
9
|
export const SHAPES = [
|
|
4
10
|
{
|
|
5
11
|
icon: SelectionIcon,
|
|
@@ -82,3 +88,54 @@ export const findShapeByKey = (key) => {
|
|
|
82
88
|
});
|
|
83
89
|
return shape?.value || null;
|
|
84
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* get the pure geometric shape of an excalidraw element
|
|
93
|
+
* which is then used for hit detection
|
|
94
|
+
*/
|
|
95
|
+
export const getElementShape = (element, elementsMap) => {
|
|
96
|
+
switch (element.type) {
|
|
97
|
+
case "rectangle":
|
|
98
|
+
case "diamond":
|
|
99
|
+
case "frame":
|
|
100
|
+
case "magicframe":
|
|
101
|
+
case "embeddable":
|
|
102
|
+
case "image":
|
|
103
|
+
case "iframe":
|
|
104
|
+
case "text":
|
|
105
|
+
case "selection":
|
|
106
|
+
return getPolygonShape(element);
|
|
107
|
+
case "arrow":
|
|
108
|
+
case "line": {
|
|
109
|
+
const roughShape = ShapeCache.get(element)?.[0] ??
|
|
110
|
+
ShapeCache.generateElementShape(element, null)[0];
|
|
111
|
+
const [, , , , cx, cy] = getElementAbsoluteCoords(element, elementsMap);
|
|
112
|
+
return shouldTestInside(element)
|
|
113
|
+
? getClosedCurveShape(element, roughShape, [element.x, element.y], element.angle, [cx, cy])
|
|
114
|
+
: getCurveShape(roughShape, [element.x, element.y], element.angle, [
|
|
115
|
+
cx,
|
|
116
|
+
cy,
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
case "ellipse":
|
|
120
|
+
return getEllipseShape(element);
|
|
121
|
+
case "freedraw": {
|
|
122
|
+
const [, , , , cx, cy] = getElementAbsoluteCoords(element, elementsMap);
|
|
123
|
+
return getFreedrawShape(element, [cx, cy], shouldTestInside(element));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
export const getBoundTextShape = (element, elementsMap) => {
|
|
128
|
+
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
129
|
+
if (boundTextElement) {
|
|
130
|
+
if (element.type === "arrow") {
|
|
131
|
+
return getElementShape({
|
|
132
|
+
...boundTextElement,
|
|
133
|
+
// arrow's bound text accurate position is not stored in the element's property
|
|
134
|
+
// but rather calculated and returned from the following static method
|
|
135
|
+
...LinearElementEditor.getBoundTextElementPosition(element, boundTextElement, elementsMap),
|
|
136
|
+
}, elementsMap);
|
|
137
|
+
}
|
|
138
|
+
return getElementShape(boundTextElement, elementsMap);
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
};
|
|
@@ -119,6 +119,7 @@ export type StaticCanvasAppState = Readonly<_CommonCanvasAppState & {
|
|
|
119
119
|
selectedElementsAreBeingDragged: AppState["selectedElementsAreBeingDragged"];
|
|
120
120
|
gridSize: AppState["gridSize"];
|
|
121
121
|
frameRendering: AppState["frameRendering"];
|
|
122
|
+
currentHoveredFontFamily: AppState["currentHoveredFontFamily"];
|
|
122
123
|
}>;
|
|
123
124
|
export type InteractiveCanvasAppState = Readonly<_CommonCanvasAppState & {
|
|
124
125
|
activeEmbeddable: AppState["activeEmbeddable"];
|
|
@@ -205,7 +206,9 @@ export interface AppState {
|
|
|
205
206
|
currentItemTextAlign: TextAlign;
|
|
206
207
|
currentItemStartArrowhead: Arrowhead | null;
|
|
207
208
|
currentItemEndArrowhead: Arrowhead | null;
|
|
209
|
+
currentHoveredFontFamily: FontFamilyValues | null;
|
|
208
210
|
currentItemRoundness: StrokeRoundness;
|
|
211
|
+
currentItemArrowType: "sharp" | "round" | "elbow";
|
|
209
212
|
viewBackgroundColor: string;
|
|
210
213
|
scrollX: number;
|
|
211
214
|
scrollY: number;
|
|
@@ -216,7 +219,7 @@ export interface AppState {
|
|
|
216
219
|
isRotating: boolean;
|
|
217
220
|
zoom: Zoom;
|
|
218
221
|
openMenu: "canvas" | "shape" | null;
|
|
219
|
-
openPopup: "canvasBackground" | "elementBackground" | "elementStroke" | null;
|
|
222
|
+
openPopup: "canvasBackground" | "elementBackground" | "elementStroke" | "fontFamily" | null;
|
|
220
223
|
openSidebar: {
|
|
221
224
|
name: SidebarName;
|
|
222
225
|
tab?: SidebarTabName;
|
|
@@ -350,7 +353,7 @@ export type OnUserFollowedPayload = {
|
|
|
350
353
|
};
|
|
351
354
|
export interface ExcalidrawProps {
|
|
352
355
|
onChange?: (elements: readonly OrderedExcalidrawElement[], appState: AppState, files: BinaryFiles) => void;
|
|
353
|
-
initialData?: MaybePromise<ExcalidrawInitialDataState | null>;
|
|
356
|
+
initialData?: (() => MaybePromise<ExcalidrawInitialDataState | null>) | MaybePromise<ExcalidrawInitialDataState | null>;
|
|
354
357
|
excalidrawAPI?: (api: ExcalidrawImperativeAPI) => void;
|
|
355
358
|
isCollaborating?: boolean;
|
|
356
359
|
onPointerUpdate?: (payload: {
|
|
@@ -390,6 +393,7 @@ export interface ExcalidrawProps {
|
|
|
390
393
|
validateEmbeddable?: boolean | string[] | RegExp | RegExp[] | ((link: string) => boolean | undefined);
|
|
391
394
|
renderEmbeddable?: (element: NonDeleted<ExcalidrawEmbeddableElement>, appState: AppState) => JSX.Element | null;
|
|
392
395
|
aiEnabled?: boolean;
|
|
396
|
+
showDeprecatedFonts?: boolean;
|
|
393
397
|
}
|
|
394
398
|
export type SceneData = {
|
|
395
399
|
elements?: ImportedDataState["elements"];
|
|
@@ -454,6 +458,7 @@ export type AppClassProperties = {
|
|
|
454
458
|
device: App["device"];
|
|
455
459
|
scene: App["scene"];
|
|
456
460
|
syncActionResult: App["syncActionResult"];
|
|
461
|
+
fonts: App["fonts"];
|
|
457
462
|
pasteFromClipboard: App["pasteFromClipboard"];
|
|
458
463
|
id: App["id"];
|
|
459
464
|
onInsertElements: App["onInsertElements"];
|
|
@@ -468,8 +473,8 @@ export type AppClassProperties = {
|
|
|
468
473
|
setOpenDialog: App["setOpenDialog"];
|
|
469
474
|
insertEmbeddableElement: App["insertEmbeddableElement"];
|
|
470
475
|
onMagicframeToolSelect: App["onMagicframeToolSelect"];
|
|
471
|
-
getElementShape: App["getElementShape"];
|
|
472
476
|
getName: App["getName"];
|
|
477
|
+
dismissLinearEditor: App["dismissLinearEditor"];
|
|
473
478
|
};
|
|
474
479
|
export type PointerDownState = Readonly<{
|
|
475
480
|
origin: Readonly<{
|