@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
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { generateNKeysBetween } from "fractional-indexing";
|
|
2
2
|
import { mutateElement } from "./element/mutateElement";
|
|
3
3
|
import { InvalidFractionalIndexError } from "./errors";
|
|
4
|
+
import { hasBoundTextElement } from "./element/typeChecks";
|
|
5
|
+
import { getBoundTextElement } from "./element/textElement";
|
|
6
|
+
import { arrayToMap } from "./utils";
|
|
4
7
|
/**
|
|
5
8
|
* Envisioned relation between array order and fractional indices:
|
|
6
9
|
*
|
|
7
10
|
* 1) Array (or array-like ordered data structure) should be used as a cache of elements order, hiding the internal fractional indices implementation.
|
|
8
|
-
* - it's undesirable to
|
|
11
|
+
* - it's undesirable to perform reorder for each related operation, therefore it's necessary to cache the order defined by fractional indices into an ordered data structure
|
|
9
12
|
* - it's easy enough to define the order of the elements from the outside (boundaries), without worrying about the underlying structure of fractional indices (especially for the host apps)
|
|
10
13
|
* - it's necessary to always keep the array support for backwards compatibility (restore) - old scenes, old libraries, supporting multiple excalidraw versions etc.
|
|
11
14
|
* - it's necessary to always keep the fractional indices in sync with the array order
|
|
12
15
|
* - elements with invalid indices should be detected and synced, without altering the already valid indices
|
|
13
16
|
*
|
|
14
17
|
* 2) Fractional indices should be used to reorder the elements, whenever the cached order is expected to be invalidated.
|
|
15
|
-
* - as the fractional indices are encoded as part of the elements, it opens up
|
|
16
|
-
* - re-order based on fractional indices should be part of (multiplayer) operations such as
|
|
18
|
+
* - as the fractional indices are encoded as part of the elements, it opens up possibilities for incremental-like APIs
|
|
19
|
+
* - re-order based on fractional indices should be part of (multiplayer) operations such as reconciliation & undo/redo
|
|
17
20
|
* - technically all the z-index actions could perform also re-order based on fractional indices,but in current state it would not bring much benefits,
|
|
18
21
|
* as it's faster & more efficient to perform re-order based on array manipulation and later synchronisation of moved indices with the array order
|
|
19
22
|
*/
|
|
@@ -22,12 +25,38 @@ import { InvalidFractionalIndexError } from "./errors";
|
|
|
22
25
|
*
|
|
23
26
|
* @throws `InvalidFractionalIndexError` if invalid index is detected.
|
|
24
27
|
*/
|
|
25
|
-
export const validateFractionalIndices = (
|
|
28
|
+
export const validateFractionalIndices = (elements, { shouldThrow = false, includeBoundTextValidation = false, reconciliationContext, }) => {
|
|
29
|
+
const errorMessages = [];
|
|
30
|
+
const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
|
|
31
|
+
const indices = elements.map((x) => x.index);
|
|
26
32
|
for (const [i, index] of indices.entries()) {
|
|
27
33
|
const predecessorIndex = indices[i - 1];
|
|
28
34
|
const successorIndex = indices[i + 1];
|
|
29
35
|
if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
|
|
30
|
-
|
|
36
|
+
errorMessages.push(`Fractional indices invariant has been compromised: "${stringifyElement(elements[i - 1])}", "${stringifyElement(elements[i])}", "${stringifyElement(elements[i + 1])}"`);
|
|
37
|
+
}
|
|
38
|
+
// disabled by default, as we don't fix it
|
|
39
|
+
if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
|
|
40
|
+
const container = elements[i];
|
|
41
|
+
const text = getBoundTextElement(container, arrayToMap(elements));
|
|
42
|
+
if (text && text.index <= container.index) {
|
|
43
|
+
errorMessages.push(`Fractional indices invariant for bound elements has been compromised: "${stringifyElement(text)}", "${stringifyElement(container)}"`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (errorMessages.length) {
|
|
48
|
+
const error = new InvalidFractionalIndexError();
|
|
49
|
+
const additionalContext = [];
|
|
50
|
+
if (reconciliationContext) {
|
|
51
|
+
additionalContext.push("Additional reconciliation context:");
|
|
52
|
+
additionalContext.push(reconciliationContext.localElements.map((x) => stringifyElement(x)));
|
|
53
|
+
additionalContext.push(reconciliationContext.remoteElements.map((x) => stringifyElement(x)));
|
|
54
|
+
}
|
|
55
|
+
// report just once and with the stacktrace
|
|
56
|
+
console.error(errorMessages.join("\n\n"), error.stack, elements.map((x) => stringifyElement(x)), ...additionalContext);
|
|
57
|
+
if (shouldThrow) {
|
|
58
|
+
// if enabled, gather all the errors first, throw once
|
|
59
|
+
throw error;
|
|
31
60
|
}
|
|
32
61
|
}
|
|
33
62
|
};
|
|
@@ -62,8 +91,11 @@ export const syncMovedIndices = (elements, movedElements) => {
|
|
|
62
91
|
const indicesGroups = getMovedIndicesGroups(elements, movedElements);
|
|
63
92
|
// try generatating indices, throws on invalid movedElements
|
|
64
93
|
const elementsUpdates = generateIndices(elements, indicesGroups);
|
|
94
|
+
const elementsCandidates = elements.map((x) => elementsUpdates.has(x) ? { ...x, ...elementsUpdates.get(x) } : x);
|
|
65
95
|
// ensure next indices are valid before mutation, throws on invalid ones
|
|
66
|
-
validateFractionalIndices(
|
|
96
|
+
validateFractionalIndices(elementsCandidates,
|
|
97
|
+
// we don't autofix invalid bound text indices, hence don't include it in the validation
|
|
98
|
+
{ includeBoundTextValidation: false, shouldThrow: true });
|
|
67
99
|
// split mutation so we don't end up in an incosistent state
|
|
68
100
|
for (const [element, update] of elementsUpdates) {
|
|
69
101
|
mutateElement(element, update, false);
|
|
@@ -59,5 +59,5 @@ export declare const omitGroupsContainingFrameLikes: (allElements: ElementsMapOr
|
|
|
59
59
|
*/
|
|
60
60
|
export declare const getTargetFrame: (element: ExcalidrawElement, elementsMap: ElementsMap, appState: StaticCanvasAppState) => import("./element/types").ExcalidrawFrameElement | import("./element/types").ExcalidrawMagicFrameElement | null;
|
|
61
61
|
export declare const isElementInFrame: (element: ExcalidrawElement, allElementsMap: ElementsMap, appState: StaticCanvasAppState) => boolean;
|
|
62
|
-
export declare const getFrameLikeTitle: (element: ExcalidrawFrameLikeElement
|
|
62
|
+
export declare const getFrameLikeTitle: (element: ExcalidrawFrameLikeElement) => string;
|
|
63
63
|
export declare const getElementsOverlappingFrame: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => NonDeletedExcalidrawElement[];
|
package/dist/excalidraw/frame.js
CHANGED
|
@@ -426,12 +426,12 @@ export const isElementInFrame = (element, allElementsMap, appState) => {
|
|
|
426
426
|
}
|
|
427
427
|
return false;
|
|
428
428
|
};
|
|
429
|
-
export const getFrameLikeTitle = (element
|
|
429
|
+
export const getFrameLikeTitle = (element) => {
|
|
430
430
|
// TODO name frames "AI" only if specific to AI frames
|
|
431
431
|
return element.name === null
|
|
432
432
|
? isFrameElement(element)
|
|
433
|
-
?
|
|
434
|
-
:
|
|
433
|
+
? "Frame"
|
|
434
|
+
: "AI Frame"
|
|
435
435
|
: element.name;
|
|
436
436
|
};
|
|
437
437
|
export const getElementsOverlappingFrame = (elements, frame) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AppStateChange, ElementsChange } from "./change";
|
|
2
2
|
import type { SceneElementsMap } from "./element/types";
|
|
3
3
|
import { Emitter } from "./emitter";
|
|
4
|
+
import type Scene from "./scene/Scene";
|
|
4
5
|
import type { Snapshot } from "./store";
|
|
5
6
|
import type { AppState } from "./types";
|
|
6
7
|
export declare class HistoryChangedEvent {
|
|
@@ -19,8 +20,8 @@ export declare class History {
|
|
|
19
20
|
* Record a local change which will go into the history
|
|
20
21
|
*/
|
|
21
22
|
record(elementsChange: ElementsChange, appStateChange: AppStateChange): void;
|
|
22
|
-
undo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot
|
|
23
|
-
redo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot
|
|
23
|
+
undo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>, scene: Scene): void | [SceneElementsMap, AppState];
|
|
24
|
+
redo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>, scene: Scene): void | [SceneElementsMap, AppState];
|
|
24
25
|
private perform;
|
|
25
26
|
private static pop;
|
|
26
27
|
private static push;
|
|
@@ -31,7 +32,7 @@ export declare class HistoryEntry {
|
|
|
31
32
|
private constructor();
|
|
32
33
|
static create(appStateChange: AppStateChange, elementsChange: ElementsChange): HistoryEntry;
|
|
33
34
|
inverse(): HistoryEntry;
|
|
34
|
-
applyTo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot
|
|
35
|
+
applyTo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>, scene: Scene): [SceneElementsMap, AppState, boolean];
|
|
35
36
|
/**
|
|
36
37
|
* Apply latest (remote) changes to the history entry, creates new instance of `HistoryEntry`.
|
|
37
38
|
*/
|
|
@@ -38,13 +38,13 @@ export class History {
|
|
|
38
38
|
this.onHistoryChangedEmitter.trigger(new HistoryChangedEvent(this.isUndoStackEmpty, this.isRedoStackEmpty));
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
undo(elements, appState, snapshot) {
|
|
42
|
-
return this.perform(elements, appState, snapshot, () => History.pop(this.undoStack), (entry) => History.push(this.redoStack, entry, elements));
|
|
41
|
+
undo(elements, appState, snapshot, scene) {
|
|
42
|
+
return this.perform(elements, appState, snapshot, () => History.pop(this.undoStack), (entry) => History.push(this.redoStack, entry, elements), scene);
|
|
43
43
|
}
|
|
44
|
-
redo(elements, appState, snapshot) {
|
|
45
|
-
return this.perform(elements, appState, snapshot, () => History.pop(this.redoStack), (entry) => History.push(this.undoStack, entry, elements));
|
|
44
|
+
redo(elements, appState, snapshot, scene) {
|
|
45
|
+
return this.perform(elements, appState, snapshot, () => History.pop(this.redoStack), (entry) => History.push(this.undoStack, entry, elements), scene);
|
|
46
46
|
}
|
|
47
|
-
perform(elements, appState, snapshot, pop, push) {
|
|
47
|
+
perform(elements, appState, snapshot, pop, push, scene) {
|
|
48
48
|
try {
|
|
49
49
|
let historyEntry = pop();
|
|
50
50
|
if (historyEntry === null) {
|
|
@@ -57,7 +57,7 @@ export class History {
|
|
|
57
57
|
while (historyEntry) {
|
|
58
58
|
try {
|
|
59
59
|
[nextElements, nextAppState, containsVisibleChange] =
|
|
60
|
-
historyEntry.applyTo(nextElements, nextAppState, snapshot);
|
|
60
|
+
historyEntry.applyTo(nextElements, nextAppState, snapshot, scene);
|
|
61
61
|
}
|
|
62
62
|
finally {
|
|
63
63
|
// make sure to always push / pop, even if the increment is corrupted
|
|
@@ -104,8 +104,8 @@ export class HistoryEntry {
|
|
|
104
104
|
inverse() {
|
|
105
105
|
return new HistoryEntry(this.appStateChange.inverse(), this.elementsChange.inverse());
|
|
106
106
|
}
|
|
107
|
-
applyTo(elements, appState, snapshot) {
|
|
108
|
-
const [nextElements, elementsContainVisibleChange] = this.elementsChange.applyTo(elements, snapshot.elements);
|
|
107
|
+
applyTo(elements, appState, snapshot, scene) {
|
|
108
|
+
const [nextElements, elementsContainVisibleChange] = this.elementsChange.applyTo(elements, snapshot.elements, scene);
|
|
109
109
|
const [nextAppState, appStateContainsVisibleChange] = this.appStateChange.applyTo(appState, nextElements);
|
|
110
110
|
const appliedVisibleChanges = elementsContainVisibleChange || appStateContainsVisibleChange;
|
|
111
111
|
return [nextElements, nextAppState, appliedVisibleChanges];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import "./css/app.scss";
|
|
3
3
|
import "./css/styles.scss";
|
|
4
|
-
import "
|
|
4
|
+
import "./fonts/assets/fonts.css";
|
|
5
5
|
import type { ExcalidrawProps } from "./types";
|
|
6
6
|
import Footer from "./components/footer/FooterCenter";
|
|
7
7
|
import MainMenu from "./components/main-menu/MainMenu";
|
package/dist/excalidraw/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import App from "./components/App";
|
|
|
5
5
|
import { isShallowEqual } from "./utils";
|
|
6
6
|
import "./css/app.scss";
|
|
7
7
|
import "./css/styles.scss";
|
|
8
|
-
import "
|
|
8
|
+
import "./fonts/assets/fonts.css";
|
|
9
9
|
import polyfill from "./polyfill";
|
|
10
10
|
import { defaultLang } from "./i18n";
|
|
11
11
|
import { DEFAULT_UI_OPTIONS } from "./constants";
|
|
@@ -17,7 +17,7 @@ import WelcomeScreen from "./components/welcome-screen/WelcomeScreen";
|
|
|
17
17
|
import LiveCollaborationTrigger from "./components/live-collaboration/LiveCollaborationTrigger";
|
|
18
18
|
polyfill();
|
|
19
19
|
const ExcalidrawBase = (props) => {
|
|
20
|
-
const { onChange, initialData, excalidrawAPI, isCollaborating = false, onPointerUpdate, renderTopRightUI, langCode = defaultLang.code, viewModeEnabled, zenModeEnabled, gridModeEnabled, libraryReturnUrl, theme, name, renderCustomStats, onPaste, detectScroll = true, handleKeyboardGlobally = false, onLibraryChange, autoFocus = false, generateIdForFile, onLinkOpen, onPointerDown, onPointerUp, onScrollChange, children, validateEmbeddable, renderEmbeddable, aiEnabled, } = props;
|
|
20
|
+
const { onChange, initialData, excalidrawAPI, isCollaborating = false, onPointerUpdate, renderTopRightUI, langCode = defaultLang.code, viewModeEnabled, zenModeEnabled, gridModeEnabled, libraryReturnUrl, theme, name, renderCustomStats, onPaste, detectScroll = true, handleKeyboardGlobally = false, onLibraryChange, autoFocus = false, generateIdForFile, onLinkOpen, onPointerDown, onPointerUp, onScrollChange, children, validateEmbeddable, renderEmbeddable, aiEnabled, showDeprecatedFonts, } = props;
|
|
21
21
|
const canvasActions = props.UIOptions?.canvasActions;
|
|
22
22
|
// FIXME normalize/set defaults in parent component so that the memo resolver
|
|
23
23
|
// compares the same values
|
|
@@ -60,7 +60,7 @@ const ExcalidrawBase = (props) => {
|
|
|
60
60
|
document.removeEventListener("touchmove", handleTouchMove);
|
|
61
61
|
};
|
|
62
62
|
}, []);
|
|
63
|
-
return (_jsx(Provider, { unstable_createStore: () => jotaiStore, scope: jotaiScope, children: _jsx(InitializeApp, { langCode: langCode, theme: theme, children: _jsx(App, { onChange: onChange, initialData: initialData, excalidrawAPI: excalidrawAPI, isCollaborating: isCollaborating, onPointerUpdate: onPointerUpdate, renderTopRightUI: renderTopRightUI, langCode: langCode, viewModeEnabled: viewModeEnabled, zenModeEnabled: zenModeEnabled, gridModeEnabled: gridModeEnabled, libraryReturnUrl: libraryReturnUrl, theme: theme, name: name, renderCustomStats: renderCustomStats, UIOptions: UIOptions, onPaste: onPaste, detectScroll: detectScroll, handleKeyboardGlobally: handleKeyboardGlobally, onLibraryChange: onLibraryChange, autoFocus: autoFocus, generateIdForFile: generateIdForFile, onLinkOpen: onLinkOpen, onPointerDown: onPointerDown, onPointerUp: onPointerUp, onScrollChange: onScrollChange, validateEmbeddable: validateEmbeddable, renderEmbeddable: renderEmbeddable, aiEnabled: aiEnabled !== false, children: children }) }) }));
|
|
63
|
+
return (_jsx(Provider, { unstable_createStore: () => jotaiStore, scope: jotaiScope, children: _jsx(InitializeApp, { langCode: langCode, theme: theme, children: _jsx(App, { onChange: onChange, initialData: initialData, excalidrawAPI: excalidrawAPI, isCollaborating: isCollaborating, onPointerUpdate: onPointerUpdate, renderTopRightUI: renderTopRightUI, langCode: langCode, viewModeEnabled: viewModeEnabled, zenModeEnabled: zenModeEnabled, gridModeEnabled: gridModeEnabled, libraryReturnUrl: libraryReturnUrl, theme: theme, name: name, renderCustomStats: renderCustomStats, UIOptions: UIOptions, onPaste: onPaste, detectScroll: detectScroll, handleKeyboardGlobally: handleKeyboardGlobally, onLibraryChange: onLibraryChange, autoFocus: autoFocus, generateIdForFile: generateIdForFile, onLinkOpen: onLinkOpen, onPointerDown: onPointerDown, onPointerUp: onPointerUp, onScrollChange: onScrollChange, validateEmbeddable: validateEmbeddable, renderEmbeddable: renderEmbeddable, aiEnabled: aiEnabled !== false, showDeprecatedFonts: showDeprecatedFonts, children: children }) }) }));
|
|
64
64
|
};
|
|
65
65
|
const areEqual = (prevProps, nextProps) => {
|
|
66
66
|
// short-circuit early
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"arrowhead_triangle_outline": "Triangle (outline)",
|
|
47
47
|
"arrowhead_diamond": "Diamond",
|
|
48
48
|
"arrowhead_diamond_outline": "Diamond (outline)",
|
|
49
|
+
"arrowtypes": "Arrow type",
|
|
50
|
+
"arrowtype_sharp": "Sharp arrow",
|
|
51
|
+
"arrowtype_round": "Curved arrow",
|
|
52
|
+
"arrowtype_elbowed": "Elbow arrow",
|
|
49
53
|
"fontSize": "Font size",
|
|
50
54
|
"fontFamily": "Font family",
|
|
51
55
|
"addWatermark": "Add \"Made with Excalidraw\"",
|
|
@@ -109,6 +113,7 @@
|
|
|
109
113
|
"share": "Share",
|
|
110
114
|
"showStroke": "Show stroke color picker",
|
|
111
115
|
"showBackground": "Show background color picker",
|
|
116
|
+
"showFonts": "Show font picker",
|
|
112
117
|
"toggleTheme": "Toggle light/dark theme",
|
|
113
118
|
"theme": "Theme",
|
|
114
119
|
"personalLib": "Personal Library",
|
|
@@ -294,6 +299,7 @@
|
|
|
294
299
|
"hints": {
|
|
295
300
|
"canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging, or use the hand tool",
|
|
296
301
|
"linearElement": "Click to start multiple points, drag for single line",
|
|
302
|
+
"arrowTool": "Click to start multiple points, drag for single line. Press {{arrowShortcut}} again to change arrow type.",
|
|
297
303
|
"freeDraw": "Click and drag, release when you're finished",
|
|
298
304
|
"text": "Tip: you can also add text by double-clicking anywhere with the selection tool",
|
|
299
305
|
"embeddable": "Click-drag to create a website embed",
|
|
@@ -557,11 +563,19 @@
|
|
|
557
563
|
"syntax": "Mermaid Syntax",
|
|
558
564
|
"preview": "Preview"
|
|
559
565
|
},
|
|
560
|
-
"
|
|
561
|
-
"
|
|
562
|
-
|
|
563
|
-
|
|
566
|
+
"quickSearch": {
|
|
567
|
+
"placeholder": "Quick search"
|
|
568
|
+
},
|
|
569
|
+
"fontList": {
|
|
570
|
+
"badge": {
|
|
571
|
+
"old": "old"
|
|
564
572
|
},
|
|
573
|
+
"sceneFonts": "In this scene",
|
|
574
|
+
"availableFonts": "Available fonts",
|
|
575
|
+
"empty": "No fonts found"
|
|
576
|
+
},
|
|
577
|
+
"userList": {
|
|
578
|
+
"empty": "No users found",
|
|
565
579
|
"hint": {
|
|
566
580
|
"text": "Click on user to follow",
|
|
567
581
|
"followStatus": "You're currently following this user",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Point, Zoom } from "./types";
|
|
2
2
|
import type { ExcalidrawElement, ExcalidrawLinearElement, NonDeleted } from "./element/types";
|
|
3
|
+
import type { Bounds } from "./element/bounds";
|
|
3
4
|
export declare const rotate: (x: number, y: number, cx: number, cy: number, angle: number) => [number, number];
|
|
4
5
|
export declare const rotatePoint: (point: readonly [number, number], center: readonly [number, number], angle: number) => [number, number];
|
|
5
6
|
export declare const adjustXYWithRotation: (sides: {
|
|
@@ -14,6 +15,7 @@ export declare const getPointOnAPath: (point: readonly [number, number], path: P
|
|
|
14
15
|
segment: number;
|
|
15
16
|
} | null;
|
|
16
17
|
export declare const distance2d: (x1: number, y1: number, x2: number, y2: number) => number;
|
|
18
|
+
export declare const distanceSq2d: (p1: readonly [number, number], p2: readonly [number, number]) => number;
|
|
17
19
|
export declare const centerPoint: (a: readonly [number, number], b: readonly [number, number]) => readonly [number, number];
|
|
18
20
|
export declare const isPathALoop: (points: ExcalidrawLinearElement["points"], zoomValue?: Zoom["value"]) => boolean;
|
|
19
21
|
export declare const isPointInPolygon: (points: Point[], x: number, y: number) => boolean;
|
|
@@ -33,3 +35,44 @@ export declare const degreeToRadian: (d: number) => number;
|
|
|
33
35
|
export declare const rangesOverlap: ([a0, a1]: [number, number], [b0, b1]: [number, number]) => boolean;
|
|
34
36
|
export declare const rangeIntersection: (rangeA: [number, number], rangeB: [number, number]) => [number, number] | null;
|
|
35
37
|
export declare const isValueInRange: (value: number, min: number, max: number) => boolean;
|
|
38
|
+
export declare const translatePoint: (p: readonly [number, number], v: import("../utils/geometry/shape").Point) => readonly [number, number];
|
|
39
|
+
export declare const scaleVector: (v: import("../utils/geometry/shape").Point, scalar: number) => import("../utils/geometry/shape").Point;
|
|
40
|
+
export declare const pointToVector: (p: readonly [number, number], origin?: readonly [number, number]) => import("../utils/geometry/shape").Point;
|
|
41
|
+
export declare const scalePointFromOrigin: (p: readonly [number, number], mid: readonly [number, number], multiplier: number) => readonly [number, number];
|
|
42
|
+
export declare const PointInTriangle: (pt: readonly [number, number], v1: readonly [number, number], v2: readonly [number, number], v3: readonly [number, number]) => boolean;
|
|
43
|
+
export declare const magnitudeSq: (vector: import("../utils/geometry/shape").Point) => number;
|
|
44
|
+
export declare const magnitude: (vector: import("../utils/geometry/shape").Point) => number;
|
|
45
|
+
export declare const normalize: (vector: import("../utils/geometry/shape").Point) => import("../utils/geometry/shape").Point;
|
|
46
|
+
export declare const addVectors: (vec1: readonly [number, number], vec2: readonly [number, number]) => import("../utils/geometry/shape").Point;
|
|
47
|
+
export declare const subtractVectors: (vec1: readonly [number, number], vec2: readonly [number, number]) => import("../utils/geometry/shape").Point;
|
|
48
|
+
export declare const pointInsideBounds: (p: readonly [number, number], bounds: Bounds) => boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Get the axis-aligned bounding box for a given element
|
|
51
|
+
*/
|
|
52
|
+
export declare const aabbForElement: (element: Readonly<ExcalidrawElement>, offset?: [number, number, number, number]) => Bounds;
|
|
53
|
+
type PolarCoords = [number, number];
|
|
54
|
+
/**
|
|
55
|
+
* Return the polar coordinates for the given carthesian point represented by
|
|
56
|
+
* (x, y) for the center point 0,0 where the first number returned is the radius,
|
|
57
|
+
* the second is the angle in radians.
|
|
58
|
+
*/
|
|
59
|
+
export declare const carthesian2Polar: ([x, y]: readonly [number, number]) => PolarCoords;
|
|
60
|
+
/**
|
|
61
|
+
* Angles are in radians and centered on 0, 0. Zero radians on a 1 radius circle
|
|
62
|
+
* corresponds to (1, 0) carthesian coordinates (point), i.e. to the "right".
|
|
63
|
+
*/
|
|
64
|
+
type SymmetricArc = {
|
|
65
|
+
radius: number;
|
|
66
|
+
startAngle: number;
|
|
67
|
+
endAngle: number;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Determines if a carthesian point lies on a symmetric arc, i.e. an arc which
|
|
71
|
+
* is part of a circle contour centered on 0, 0.
|
|
72
|
+
*/
|
|
73
|
+
export declare const isPointOnSymmetricArc: ({ radius: arcRadius, startAngle, endAngle }: SymmetricArc, point: readonly [number, number]) => boolean;
|
|
74
|
+
export declare const getCenterForBounds: (bounds: Bounds) => readonly [number, number];
|
|
75
|
+
export declare const getCenterForElement: (element: ExcalidrawElement) => readonly [number, number];
|
|
76
|
+
export declare const aabbsOverlapping: (a: Bounds, b: Bounds) => boolean;
|
|
77
|
+
export declare const clamp: (value: number, min: number, max: number) => number;
|
|
78
|
+
export {};
|
package/dist/excalidraw/math.js
CHANGED
|
@@ -102,6 +102,11 @@ export const distance2d = (x1, y1, x2, y2) => {
|
|
|
102
102
|
const yd = y2 - y1;
|
|
103
103
|
return Math.hypot(xd, yd);
|
|
104
104
|
};
|
|
105
|
+
export const distanceSq2d = (p1, p2) => {
|
|
106
|
+
const xd = p2[0] - p1[0];
|
|
107
|
+
const yd = p2[1] - p1[1];
|
|
108
|
+
return xd * xd + yd * yd;
|
|
109
|
+
};
|
|
105
110
|
export const centerPoint = (a, b) => {
|
|
106
111
|
return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
107
112
|
};
|
|
@@ -368,3 +373,108 @@ export const rangeIntersection = (rangeA, rangeB) => {
|
|
|
368
373
|
export const isValueInRange = (value, min, max) => {
|
|
369
374
|
return value >= min && value <= max;
|
|
370
375
|
};
|
|
376
|
+
export const translatePoint = (p, v) => [
|
|
377
|
+
p[0] + v[0],
|
|
378
|
+
p[1] + v[1],
|
|
379
|
+
];
|
|
380
|
+
export const scaleVector = (v, scalar) => [
|
|
381
|
+
v[0] * scalar,
|
|
382
|
+
v[1] * scalar,
|
|
383
|
+
];
|
|
384
|
+
export const pointToVector = (p, origin = [0, 0]) => [
|
|
385
|
+
p[0] - origin[0],
|
|
386
|
+
p[1] - origin[1],
|
|
387
|
+
];
|
|
388
|
+
export const scalePointFromOrigin = (p, mid, multiplier) => translatePoint(mid, scaleVector(pointToVector(p, mid), multiplier));
|
|
389
|
+
const triangleSign = (p1, p2, p3) => (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
|
|
390
|
+
export const PointInTriangle = (pt, v1, v2, v3) => {
|
|
391
|
+
const d1 = triangleSign(pt, v1, v2);
|
|
392
|
+
const d2 = triangleSign(pt, v2, v3);
|
|
393
|
+
const d3 = triangleSign(pt, v3, v1);
|
|
394
|
+
const has_neg = d1 < 0 || d2 < 0 || d3 < 0;
|
|
395
|
+
const has_pos = d1 > 0 || d2 > 0 || d3 > 0;
|
|
396
|
+
return !(has_neg && has_pos);
|
|
397
|
+
};
|
|
398
|
+
export const magnitudeSq = (vector) => vector[0] * vector[0] + vector[1] * vector[1];
|
|
399
|
+
export const magnitude = (vector) => Math.sqrt(magnitudeSq(vector));
|
|
400
|
+
export const normalize = (vector) => {
|
|
401
|
+
const m = magnitude(vector);
|
|
402
|
+
return [vector[0] / m, vector[1] / m];
|
|
403
|
+
};
|
|
404
|
+
export const addVectors = (vec1, vec2) => [vec1[0] + vec2[0], vec1[1] + vec2[1]];
|
|
405
|
+
export const subtractVectors = (vec1, vec2) => [vec1[0] - vec2[0], vec1[1] - vec2[1]];
|
|
406
|
+
export const pointInsideBounds = (p, bounds) => p[0] > bounds[0] && p[0] < bounds[2] && p[1] > bounds[1] && p[1] < bounds[3];
|
|
407
|
+
/**
|
|
408
|
+
* Get the axis-aligned bounding box for a given element
|
|
409
|
+
*/
|
|
410
|
+
export const aabbForElement = (element, offset) => {
|
|
411
|
+
const bbox = {
|
|
412
|
+
minX: element.x,
|
|
413
|
+
minY: element.y,
|
|
414
|
+
maxX: element.x + element.width,
|
|
415
|
+
maxY: element.y + element.height,
|
|
416
|
+
midX: element.x + element.width / 2,
|
|
417
|
+
midY: element.y + element.height / 2,
|
|
418
|
+
};
|
|
419
|
+
const center = [bbox.midX, bbox.midY];
|
|
420
|
+
const [topLeftX, topLeftY] = rotatePoint([bbox.minX, bbox.minY], center, element.angle);
|
|
421
|
+
const [topRightX, topRightY] = rotatePoint([bbox.maxX, bbox.minY], center, element.angle);
|
|
422
|
+
const [bottomRightX, bottomRightY] = rotatePoint([bbox.maxX, bbox.maxY], center, element.angle);
|
|
423
|
+
const [bottomLeftX, bottomLeftY] = rotatePoint([bbox.minX, bbox.maxY], center, element.angle);
|
|
424
|
+
const bounds = [
|
|
425
|
+
Math.min(topLeftX, topRightX, bottomRightX, bottomLeftX),
|
|
426
|
+
Math.min(topLeftY, topRightY, bottomRightY, bottomLeftY),
|
|
427
|
+
Math.max(topLeftX, topRightX, bottomRightX, bottomLeftX),
|
|
428
|
+
Math.max(topLeftY, topRightY, bottomRightY, bottomLeftY),
|
|
429
|
+
];
|
|
430
|
+
if (offset) {
|
|
431
|
+
const [topOffset, rightOffset, downOffset, leftOffset] = offset;
|
|
432
|
+
return [
|
|
433
|
+
bounds[0] - leftOffset,
|
|
434
|
+
bounds[1] - topOffset,
|
|
435
|
+
bounds[2] + rightOffset,
|
|
436
|
+
bounds[3] + downOffset,
|
|
437
|
+
];
|
|
438
|
+
}
|
|
439
|
+
return bounds;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Return the polar coordinates for the given carthesian point represented by
|
|
443
|
+
* (x, y) for the center point 0,0 where the first number returned is the radius,
|
|
444
|
+
* the second is the angle in radians.
|
|
445
|
+
*/
|
|
446
|
+
export const carthesian2Polar = ([x, y]) => [
|
|
447
|
+
Math.hypot(x, y),
|
|
448
|
+
Math.atan2(y, x),
|
|
449
|
+
];
|
|
450
|
+
/**
|
|
451
|
+
* Determines if a carthesian point lies on a symmetric arc, i.e. an arc which
|
|
452
|
+
* is part of a circle contour centered on 0, 0.
|
|
453
|
+
*/
|
|
454
|
+
export const isPointOnSymmetricArc = ({ radius: arcRadius, startAngle, endAngle }, point) => {
|
|
455
|
+
const [radius, angle] = carthesian2Polar(point);
|
|
456
|
+
return startAngle < endAngle
|
|
457
|
+
? Math.abs(radius - arcRadius) < 0.0000001 &&
|
|
458
|
+
startAngle <= angle &&
|
|
459
|
+
endAngle >= angle
|
|
460
|
+
: startAngle <= angle || endAngle >= angle;
|
|
461
|
+
};
|
|
462
|
+
export const getCenterForBounds = (bounds) => [
|
|
463
|
+
bounds[0] + (bounds[2] - bounds[0]) / 2,
|
|
464
|
+
bounds[1] + (bounds[3] - bounds[1]) / 2,
|
|
465
|
+
];
|
|
466
|
+
export const getCenterForElement = (element) => [
|
|
467
|
+
element.x + element.width / 2,
|
|
468
|
+
element.y + element.height / 2,
|
|
469
|
+
];
|
|
470
|
+
export const aabbsOverlapping = (a, b) => pointInsideBounds([a[0], a[1]], b) ||
|
|
471
|
+
pointInsideBounds([a[2], a[1]], b) ||
|
|
472
|
+
pointInsideBounds([a[2], a[3]], b) ||
|
|
473
|
+
pointInsideBounds([a[0], a[3]], b) ||
|
|
474
|
+
pointInsideBounds([b[0], b[1]], a) ||
|
|
475
|
+
pointInsideBounds([b[2], b[1]], a) ||
|
|
476
|
+
pointInsideBounds([b[2], b[3]], a) ||
|
|
477
|
+
pointInsideBounds([b[0], b[3]], a);
|
|
478
|
+
export const clamp = (value, min, max) => {
|
|
479
|
+
return Math.min(Math.max(value, min), max);
|
|
480
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export const isMaybeMermaidDefinition = (text) => {
|
|
3
3
|
const chartTypes = [
|
|
4
4
|
"flowchart",
|
|
5
|
+
"graph",
|
|
5
6
|
"sequenceDiagram",
|
|
6
7
|
"classDiagram",
|
|
7
8
|
"stateDiagram",
|
|
@@ -21,8 +22,8 @@ export const isMaybeMermaidDefinition = (text) => {
|
|
|
21
22
|
"xychart",
|
|
22
23
|
"block",
|
|
23
24
|
];
|
|
24
|
-
const re = new RegExp(`^(?:%%{.*?}%%[\\s\\n]*)?\\b
|
|
25
|
-
.map((x) =>
|
|
26
|
-
.join("|")}\\b`);
|
|
25
|
+
const re = new RegExp(`^(?:%%{.*?}%%[\\s\\n]*)?\\b(?:${chartTypes
|
|
26
|
+
.map((x) => `\\s*${x}(-beta)?`)
|
|
27
|
+
.join("|")})\\b`);
|
|
27
28
|
return re.test(text.trim());
|
|
28
29
|
};
|
|
@@ -12,7 +12,8 @@ import { maxBindingGap } from "../element/binding";
|
|
|
12
12
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
13
13
|
import { bootstrapCanvas, fillCircle, getNormalizedCanvasDimensions, } from "./helpers";
|
|
14
14
|
import oc from "open-color";
|
|
15
|
-
import { isFrameLikeElement, isLinearElement, isTextElement, } from "../element/typeChecks";
|
|
15
|
+
import { isArrowElement, isElbowArrow, isFrameLikeElement, isLinearElement, isTextElement, } from "../element/typeChecks";
|
|
16
|
+
import { getCornerRadius } from "../math";
|
|
16
17
|
const renderLinearElementPointHighlight = (context, appState, elementsMap) => {
|
|
17
18
|
const { elementId, hoverPointIndex } = appState.selectedLinearElement;
|
|
18
19
|
if (appState.editingLinearElement?.selectedPointsIndices?.includes(hoverPointIndex)) {
|
|
@@ -86,12 +87,13 @@ const renderBindingHighlightForBindableElement = (context, element, elementsMap)
|
|
|
86
87
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
87
88
|
const width = x2 - x1;
|
|
88
89
|
const height = y2 - y1;
|
|
89
|
-
const
|
|
90
|
+
const thickness = 10;
|
|
90
91
|
// So that we don't overlap the element itself
|
|
91
92
|
const strokeOffset = 4;
|
|
92
93
|
context.strokeStyle = "rgba(0,0,0,.05)";
|
|
93
|
-
context.lineWidth =
|
|
94
|
-
const padding = strokeOffset / 2 +
|
|
94
|
+
context.lineWidth = thickness - strokeOffset;
|
|
95
|
+
const padding = strokeOffset / 2 + thickness / 2;
|
|
96
|
+
const radius = getCornerRadius(Math.min(element.width, element.height), element);
|
|
95
97
|
switch (element.type) {
|
|
96
98
|
case "rectangle":
|
|
97
99
|
case "text":
|
|
@@ -100,7 +102,7 @@ const renderBindingHighlightForBindableElement = (context, element, elementsMap)
|
|
|
100
102
|
case "embeddable":
|
|
101
103
|
case "frame":
|
|
102
104
|
case "magicframe":
|
|
103
|
-
strokeRectWithRotation(context, x1 - padding, y1 - padding, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
105
|
+
strokeRectWithRotation(context, x1 - padding, y1 - padding, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle, undefined, radius);
|
|
104
106
|
break;
|
|
105
107
|
case "diamond":
|
|
106
108
|
const side = Math.hypot(width, height);
|
|
@@ -211,6 +213,9 @@ const renderLinearPointHandles = (context, appState, element, elementsMap) => {
|
|
|
211
213
|
? POINT_HANDLE_SIZE
|
|
212
214
|
: POINT_HANDLE_SIZE / 2;
|
|
213
215
|
points.forEach((point, idx) => {
|
|
216
|
+
if (isElbowArrow(element) && idx !== 0 && idx !== points.length - 1) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
214
219
|
const isSelected = !!appState.editingLinearElement?.selectedPointsIndices?.includes(idx);
|
|
215
220
|
renderSingleLinearPoint(context, appState, point, radius, isSelected);
|
|
216
221
|
});
|
|
@@ -346,7 +351,11 @@ const _renderInteractiveScene = ({ canvas, elementsMap, visibleElements, selecte
|
|
|
346
351
|
renderLinearPointHandles(context, appState, selectedElements[0], elementsMap);
|
|
347
352
|
}
|
|
348
353
|
if (appState.selectedLinearElement &&
|
|
349
|
-
appState.selectedLinearElement.hoverPointIndex >= 0
|
|
354
|
+
appState.selectedLinearElement.hoverPointIndex >= 0 &&
|
|
355
|
+
!(isElbowArrow(selectedElements[0]) &&
|
|
356
|
+
appState.selectedLinearElement.hoverPointIndex > 0 &&
|
|
357
|
+
appState.selectedLinearElement.hoverPointIndex <
|
|
358
|
+
selectedElements[0].points.length - 1)) {
|
|
350
359
|
renderLinearElementPointHighlight(context, appState, elementsMap);
|
|
351
360
|
}
|
|
352
361
|
// Paint selected elements
|
|
@@ -366,18 +375,25 @@ const _renderInteractiveScene = ({ canvas, elementsMap, visibleElements, selecte
|
|
|
366
375
|
const selections = [];
|
|
367
376
|
for (const element of elementsMap.values()) {
|
|
368
377
|
const selectionColors = [];
|
|
369
|
-
// local user
|
|
370
|
-
if (locallySelectedIds.has(element.id) &&
|
|
371
|
-
!isSelectedViaGroup(appState, element)) {
|
|
372
|
-
selectionColors.push(selectionColor);
|
|
373
|
-
}
|
|
374
|
-
// remote users
|
|
375
378
|
const remoteClients = renderConfig.remoteSelectedElementIds.get(element.id);
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
if (!(
|
|
380
|
+
// Elbow arrow elements cannot be selected when bound on either end
|
|
381
|
+
(isSingleLinearElementSelected &&
|
|
382
|
+
isArrowElement(element) &&
|
|
383
|
+
isElbowArrow(element) &&
|
|
384
|
+
(element.startBinding || element.endBinding)))) {
|
|
385
|
+
// local user
|
|
386
|
+
if (locallySelectedIds.has(element.id) &&
|
|
387
|
+
!isSelectedViaGroup(appState, element)) {
|
|
388
|
+
selectionColors.push(selectionColor);
|
|
389
|
+
}
|
|
390
|
+
// remote users
|
|
391
|
+
if (remoteClients) {
|
|
392
|
+
selectionColors.push(...remoteClients.map((socketId) => {
|
|
393
|
+
const background = getClientColor(socketId, appState.collaborators.get(socketId));
|
|
394
|
+
return background;
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
381
397
|
}
|
|
382
398
|
if (selectionColors.length) {
|
|
383
399
|
const [elementX1, elementY1, elementX2, elementY2, cx, cy] = getElementAbsoluteCoords(element, elementsMap, true);
|
|
@@ -9,11 +9,13 @@ export interface ExcalidrawElementWithCanvas {
|
|
|
9
9
|
canvas: HTMLCanvasElement;
|
|
10
10
|
theme: AppState["theme"];
|
|
11
11
|
scale: number;
|
|
12
|
+
angle: number;
|
|
12
13
|
zoomValue: AppState["zoom"]["value"];
|
|
13
14
|
canvasOffsetX: number;
|
|
14
15
|
canvasOffsetY: number;
|
|
15
16
|
boundTextElementVersion: number | null;
|
|
16
17
|
containingFrameOpacity: number;
|
|
18
|
+
boundTextCanvas: HTMLCanvasElement;
|
|
17
19
|
}
|
|
18
20
|
export declare const DEFAULT_LINK_SIZE = 14;
|
|
19
21
|
export declare const elementWithCanvasCache: WeakMap<ExcalidrawElement, ExcalidrawElementWithCanvas>;
|