@excalidraw/excalidraw 0.17.1-1d71f84 → 0.17.1-4689a6b
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 +1 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-AK7SWNLN.js → chunk-23CKV3WP.js} +4 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-23CKV3WP.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-RWZVJAQU.js → chunk-7D5BMEAB.js} +2227 -1976
- package/dist/browser/dev/excalidraw-assets-dev/chunk-7D5BMEAB.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js → en-W7TECCRB.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js → image-JKT6GXZD.js} +2 -2
- package/dist/browser/dev/index.css +20 -0
- package/dist/browser/dev/index.css.map +2 -2
- package/dist/browser/dev/index.js +770 -585
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/chunk-DWOM5R6H.js +55 -0
- package/dist/browser/prod/excalidraw-assets/{chunk-CTYINSWT.js → chunk-SK23VHAR.js} +2 -2
- package/dist/browser/prod/excalidraw-assets/{en-LROPV2RN.js → en-SMMH575S.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/image-WDEQS5RL.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +22 -22
- package/dist/{prod/en-II4GK66F.json → dev/en-CVBEBUBY.json} +3 -1
- package/dist/dev/index.css +20 -0
- package/dist/dev/index.css.map +2 -2
- package/dist/dev/index.js +2383 -2074
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionBoundText.js +4 -1
- package/dist/excalidraw/actions/actionCanvas.js +3 -1
- package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -0
- package/dist/excalidraw/actions/actionExport.d.ts +1 -1
- package/dist/excalidraw/actions/actionFinalize.d.ts +1 -1
- package/dist/excalidraw/actions/actionFinalize.js +3 -3
- package/dist/excalidraw/actions/actionFlip.d.ts +3 -3
- package/dist/excalidraw/actions/actionFlip.js +6 -6
- package/dist/excalidraw/actions/actionGroup.js +4 -2
- package/dist/excalidraw/actions/actionHistory.js +3 -0
- package/dist/excalidraw/actions/actionZindex.d.ts +11 -11
- package/dist/excalidraw/actions/shortcuts.js +1 -1
- package/dist/excalidraw/analytics.js +1 -1
- package/dist/excalidraw/components/App.d.ts +13 -3
- package/dist/excalidraw/components/App.js +212 -83
- package/dist/excalidraw/components/CommandPalette/CommandPalette.js +24 -10
- package/dist/excalidraw/components/DarkModeToggle.js +3 -1
- package/dist/excalidraw/components/HelpDialog.js +8 -6
- package/dist/excalidraw/components/RadioGroup.d.ts +2 -1
- package/dist/excalidraw/components/RadioGroup.js +1 -1
- package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.js +6 -2
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +18 -0
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.js +9 -0
- package/dist/excalidraw/components/hyperlink/Hyperlink.js +3 -3
- package/dist/excalidraw/components/hyperlink/helpers.js +2 -3
- package/dist/excalidraw/components/icons.d.ts +3 -0
- package/dist/excalidraw/components/icons.js +5 -1
- package/dist/excalidraw/components/main-menu/DefaultItems.d.ts +12 -2
- package/dist/excalidraw/components/main-menu/DefaultItems.js +38 -7
- package/dist/excalidraw/constants.d.ts +0 -3
- package/dist/excalidraw/constants.js +0 -3
- package/dist/excalidraw/data/magic.js +2 -1
- package/dist/excalidraw/data/reconcile.d.ts +6 -0
- package/dist/excalidraw/data/reconcile.js +49 -0
- package/dist/excalidraw/data/restore.d.ts +3 -3
- package/dist/excalidraw/data/restore.js +5 -6
- package/dist/excalidraw/data/transform.d.ts +1 -1
- package/dist/excalidraw/data/transform.js +12 -3
- package/dist/excalidraw/element/binding.d.ts +22 -9
- package/dist/excalidraw/element/binding.js +403 -26
- package/dist/excalidraw/element/bounds.d.ts +0 -1
- package/dist/excalidraw/element/bounds.js +0 -3
- package/dist/excalidraw/element/collision.d.ts +14 -19
- package/dist/excalidraw/element/collision.js +36 -713
- package/dist/excalidraw/element/embeddable.js +18 -43
- package/dist/excalidraw/element/index.d.ts +0 -1
- package/dist/excalidraw/element/index.js +0 -1
- package/dist/excalidraw/element/linearElementEditor.d.ts +10 -10
- package/dist/excalidraw/element/linearElementEditor.js +6 -4
- package/dist/excalidraw/element/newElement.d.ts +1 -1
- package/dist/excalidraw/element/newElement.js +2 -1
- package/dist/excalidraw/element/textElement.d.ts +0 -1
- package/dist/excalidraw/element/textElement.js +0 -30
- package/dist/excalidraw/element/types.d.ts +17 -2
- package/dist/excalidraw/errors.d.ts +3 -0
- package/dist/excalidraw/errors.js +3 -0
- package/dist/excalidraw/fractionalIndex.d.ts +40 -0
- package/dist/excalidraw/fractionalIndex.js +241 -0
- package/dist/excalidraw/frame.d.ts +1 -1
- package/dist/excalidraw/hooks/useCreatePortalContainer.js +2 -1
- package/dist/excalidraw/locales/en.json +3 -1
- package/dist/excalidraw/renderer/helpers.js +2 -2
- package/dist/excalidraw/renderer/interactiveScene.js +1 -1
- package/dist/excalidraw/renderer/renderElement.js +3 -3
- package/dist/excalidraw/renderer/renderSnaps.js +2 -1
- package/dist/excalidraw/scene/Scene.d.ts +7 -6
- package/dist/excalidraw/scene/Scene.js +28 -13
- package/dist/excalidraw/scene/export.js +4 -3
- package/dist/excalidraw/types.d.ts +4 -3
- package/dist/excalidraw/utils.d.ts +1 -0
- package/dist/excalidraw/utils.js +1 -0
- package/dist/excalidraw/zindex.d.ts +2 -2
- package/dist/excalidraw/zindex.js +9 -13
- package/dist/{dev/en-II4GK66F.json → prod/en-CVBEBUBY.json} +3 -1
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +36 -36
- package/dist/utils/collision.d.ts +4 -0
- package/dist/utils/collision.js +48 -0
- package/dist/utils/geometry/geometry.d.ts +71 -0
- package/dist/utils/geometry/geometry.js +674 -0
- package/dist/utils/geometry/shape.d.ts +55 -0
- package/dist/utils/geometry/shape.js +149 -0
- package/package.json +2 -1
- package/dist/browser/dev/excalidraw-assets-dev/chunk-AK7SWNLN.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-RWZVJAQU.js.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-LL4GORAM.js +0 -55
- package/dist/browser/prod/excalidraw-assets/image-EFCJDJH3.js +0 -1
- /package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js.map → en-W7TECCRB.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js.map → image-JKT6GXZD.js.map} +0 -0
|
@@ -7,14 +7,14 @@ import { wrapText } from "./textElement";
|
|
|
7
7
|
import { isIframeElement } from "./typeChecks";
|
|
8
8
|
const embeddedLinkCache = new Map();
|
|
9
9
|
const RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/;
|
|
10
|
-
const RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}
|
|
10
|
+
const RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
|
|
11
11
|
const RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
|
|
12
12
|
const RE_GH_GIST = /^https:\/\/gist\.github\.com/;
|
|
13
|
-
const RE_GH_GIST_EMBED =
|
|
13
|
+
const RE_GH_GIST_EMBED = /https?:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)\.js["']/i;
|
|
14
14
|
// not anchored to start to allow <blockquote> twitter embeds
|
|
15
|
-
const RE_TWITTER = /(?:
|
|
16
|
-
const RE_TWITTER_EMBED = /^<blockquote[\s\S]*?\shref=["'](https
|
|
17
|
-
const RE_VALTOWN = /^https:\/\/(?:www\.)?val
|
|
15
|
+
const RE_TWITTER = /(?:https?:\/\/)?(?:(?:w){3}\.)?(?:twitter|x)\.com/;
|
|
16
|
+
const RE_TWITTER_EMBED = /^<blockquote[\s\S]*?\shref=["'](https?:\/\/(?:twitter|x)\.com\/[^"']*)/i;
|
|
17
|
+
const RE_VALTOWN = /^https:\/\/(?:www\.)?val\.town\/(v|embed)\/[a-zA-Z_$][0-9a-zA-Z_$]+\.[a-zA-Z_$][0-9a-zA-Z_$]+/;
|
|
18
18
|
const RE_GENERIC_EMBED = /^<(?:iframe|blockquote)[\s\S]*?\s(?:src|href)=["']([^"']*)["'][\s\S]*?>$/i;
|
|
19
19
|
const RE_GIPHY = /giphy.com\/(?:clips|embed|gifs)\/[a-zA-Z0-9]*?-?([a-zA-Z0-9]+)(?:[^a-zA-Z0-9]|$)/;
|
|
20
20
|
const ALLOWED_DOMAINS = new Set([
|
|
@@ -117,43 +117,19 @@ export const getEmbedLink = (link) => {
|
|
|
117
117
|
if (RE_TWITTER.test(link)) {
|
|
118
118
|
// the embed srcdoc still supports twitter.com domain only
|
|
119
119
|
link = link.replace(/\bx.com\b/, "twitter.com");
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
srcdoc: () => srcDoc,
|
|
127
|
-
intrinsicSize: { w: 480, h: 480 },
|
|
128
|
-
};
|
|
129
|
-
// assume regular tweet url
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
ret = {
|
|
133
|
-
type: "document",
|
|
134
|
-
srcdoc: (theme) => createSrcDoc(`<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`),
|
|
135
|
-
intrinsicSize: { w: 480, h: 480 },
|
|
136
|
-
};
|
|
137
|
-
}
|
|
120
|
+
const ret = {
|
|
121
|
+
type: "document",
|
|
122
|
+
srcdoc: (theme) => createSrcDoc(`<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`),
|
|
123
|
+
intrinsicSize: { w: 480, h: 480 },
|
|
124
|
+
sandbox: { allowSameOrigin: true },
|
|
125
|
+
};
|
|
138
126
|
embeddedLinkCache.set(originalLink, ret);
|
|
139
127
|
return ret;
|
|
140
128
|
}
|
|
141
129
|
if (RE_GH_GIST.test(link)) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const srcDoc = createSrcDoc(link);
|
|
146
|
-
ret = {
|
|
147
|
-
type: "document",
|
|
148
|
-
srcdoc: () => srcDoc,
|
|
149
|
-
intrinsicSize: { w: 550, h: 720 },
|
|
150
|
-
};
|
|
151
|
-
// assume regular url
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
ret = {
|
|
155
|
-
type: "document",
|
|
156
|
-
srcdoc: () => createSrcDoc(`
|
|
130
|
+
const ret = {
|
|
131
|
+
type: "document",
|
|
132
|
+
srcdoc: () => createSrcDoc(`
|
|
157
133
|
<script src="${link}.js"></script>
|
|
158
134
|
<style type="text/css">
|
|
159
135
|
* { margin: 0px; }
|
|
@@ -161,9 +137,8 @@ export const getEmbedLink = (link) => {
|
|
|
161
137
|
.gist .gist-file { height: calc(100vh - 2px); padding: 0px; display: grid; grid-template-rows: 1fr auto; }
|
|
162
138
|
</style>
|
|
163
139
|
`),
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
140
|
+
intrinsicSize: { w: 550, h: 720 },
|
|
141
|
+
};
|
|
167
142
|
embeddedLinkCache.set(link, ret);
|
|
168
143
|
return ret;
|
|
169
144
|
}
|
|
@@ -249,8 +224,8 @@ export const maybeParseEmbedSrc = (str) => {
|
|
|
249
224
|
return twitterMatch[1];
|
|
250
225
|
}
|
|
251
226
|
const gistMatch = str.match(RE_GH_GIST_EMBED);
|
|
252
|
-
if (gistMatch && gistMatch.length ===
|
|
253
|
-
return gistMatch[1]
|
|
227
|
+
if (gistMatch && gistMatch.length === 3) {
|
|
228
|
+
return `https://gist.github.com/${gistMatch[1]}/${gistMatch[2]}`;
|
|
254
229
|
}
|
|
255
230
|
if (RE_GIPHY.test(str)) {
|
|
256
231
|
return `https://giphy.com/embed/${RE_GIPHY.exec(str)[1]}`;
|
|
@@ -2,7 +2,6 @@ import { ExcalidrawElement, NonDeletedExcalidrawElement, NonDeleted } from "./ty
|
|
|
2
2
|
export { newElement, newTextElement, updateTextElement, refreshTextDimensions, newLinearElement, newImageElement, duplicateElement, } from "./newElement";
|
|
3
3
|
export { getElementAbsoluteCoords, getElementBounds, getCommonBounds, getDiamondPoints, getArrowheadPoints, getClosestElementBounds, } from "./bounds";
|
|
4
4
|
export { OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, getTransformHandlesFromCoords, getTransformHandles, } from "./transformHandles";
|
|
5
|
-
export { hitTest, isHittingElementBoundingBoxWithoutHittingElement, } from "./collision";
|
|
6
5
|
export { resizeTest, getCursorForResizingElement, getElementWithTransformHandleType, getTransformHandleTypeFromCoords, } from "./resizeTest";
|
|
7
6
|
export { transformElements, getResizeOffsetXY, getResizeArrowDirection, } from "./resizeElements";
|
|
8
7
|
export { dragSelectedElements, getDragOffsetXY, dragNewElement, } from "./dragElements";
|
|
@@ -3,7 +3,6 @@ import { isLinearElementType } from "./typeChecks";
|
|
|
3
3
|
export { newElement, newTextElement, updateTextElement, refreshTextDimensions, newLinearElement, newImageElement, duplicateElement, } from "./newElement";
|
|
4
4
|
export { getElementAbsoluteCoords, getElementBounds, getCommonBounds, getDiamondPoints, getArrowheadPoints, getClosestElementBounds, } from "./bounds";
|
|
5
5
|
export { OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, getTransformHandlesFromCoords, getTransformHandles, } from "./transformHandles";
|
|
6
|
-
export { hitTest, isHittingElementBoundingBoxWithoutHittingElement, } from "./collision";
|
|
7
6
|
export { resizeTest, getCursorForResizingElement, getElementWithTransformHandleType, getTransformHandleTypeFromCoords, } from "./resizeTest";
|
|
8
7
|
export { transformElements, getResizeOffsetXY, getResizeArrowDirection, } from "./resizeElements";
|
|
9
8
|
export { dragSelectedElements, getDragOffsetXY, dragNewElement, } from "./dragElements";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { NonDeleted, ExcalidrawLinearElement, ExcalidrawElement, PointBinding, ExcalidrawBindableElement, ExcalidrawTextElementWithContainer, ElementsMap,
|
|
2
|
+
import { NonDeleted, ExcalidrawLinearElement, ExcalidrawElement, PointBinding, ExcalidrawBindableElement, ExcalidrawTextElementWithContainer, ElementsMap, NonDeletedSceneElementsMap } from "./types";
|
|
3
3
|
import { Bounds } from "./bounds";
|
|
4
|
-
import { Point, AppState, PointerCoords, InteractiveCanvasAppState } from "../types";
|
|
4
|
+
import { Point, AppState, PointerCoords, InteractiveCanvasAppState, AppClassProperties } from "../types";
|
|
5
5
|
import History from "../history";
|
|
6
6
|
declare const editorMidPointsCache: {
|
|
7
7
|
version: number | null;
|
|
@@ -52,7 +52,7 @@ export declare class LinearElementEditor {
|
|
|
52
52
|
x: number;
|
|
53
53
|
y: number;
|
|
54
54
|
}[]) => void, linearElementEditor: LinearElementEditor, elementsMap: NonDeletedSceneElementsMap): boolean;
|
|
55
|
-
static handlePointerUp(event: PointerEvent, editingLinearElement: LinearElementEditor, appState: AppState,
|
|
55
|
+
static handlePointerUp(event: PointerEvent, editingLinearElement: LinearElementEditor, appState: AppState, app: AppClassProperties): LinearElementEditor;
|
|
56
56
|
static getEditorMidPoints: (element: NonDeleted<ExcalidrawLinearElement>, elementsMap: ElementsMap, appState: InteractiveCanvasAppState) => (typeof editorMidPointsCache)["points"];
|
|
57
57
|
static updateEditorMidPointsCache: (element: NonDeleted<ExcalidrawLinearElement>, elementsMap: ElementsMap, appState: InteractiveCanvasAppState) => void;
|
|
58
58
|
static getSegmentMidpointHitCoords: (linearElementEditor: LinearElementEditor, scenePointer: {
|
|
@@ -65,7 +65,7 @@ export declare class LinearElementEditor {
|
|
|
65
65
|
static handlePointerDown(event: React.PointerEvent<HTMLElement>, appState: AppState, history: History, scenePointer: {
|
|
66
66
|
x: number;
|
|
67
67
|
y: number;
|
|
68
|
-
}, linearElementEditor: LinearElementEditor,
|
|
68
|
+
}, linearElementEditor: LinearElementEditor, app: AppClassProperties): {
|
|
69
69
|
didAddPoint: boolean;
|
|
70
70
|
hitElement: NonDeleted<ExcalidrawElement> | null;
|
|
71
71
|
linearElementEditor: LinearElementEditor | null;
|
|
@@ -133,13 +133,13 @@ export declare class LinearElementEditor {
|
|
|
133
133
|
isLoading: boolean;
|
|
134
134
|
errorMessage: import("react").ReactNode;
|
|
135
135
|
activeEmbeddable: {
|
|
136
|
-
element: NonDeletedExcalidrawElement;
|
|
136
|
+
element: import("./types").NonDeletedExcalidrawElement;
|
|
137
137
|
state: "hover" | "active";
|
|
138
138
|
} | null;
|
|
139
|
-
draggingElement: NonDeletedExcalidrawElement | null;
|
|
140
|
-
resizingElement: NonDeletedExcalidrawElement | null;
|
|
139
|
+
draggingElement: import("./types").NonDeletedExcalidrawElement | null;
|
|
140
|
+
resizingElement: import("./types").NonDeletedExcalidrawElement | null;
|
|
141
141
|
multiElement: NonDeleted<ExcalidrawLinearElement> | null;
|
|
142
|
-
selectionElement: NonDeletedExcalidrawElement | null;
|
|
142
|
+
selectionElement: import("./types").NonDeletedExcalidrawElement | null;
|
|
143
143
|
isBindingEnabled: boolean;
|
|
144
144
|
startBoundElement: NonDeleted<ExcalidrawBindableElement> | null;
|
|
145
145
|
suggestedBindings: import("./binding").SuggestedBinding[];
|
|
@@ -152,7 +152,7 @@ export declare class LinearElementEditor {
|
|
|
152
152
|
};
|
|
153
153
|
editingFrame: string | null;
|
|
154
154
|
elementsToHighlight: NonDeleted<ExcalidrawElement>[] | null;
|
|
155
|
-
editingElement: NonDeletedExcalidrawElement | null;
|
|
155
|
+
editingElement: import("./types").NonDeletedExcalidrawElement | null;
|
|
156
156
|
activeTool: {
|
|
157
157
|
lastActiveTool: import("../types").ActiveTool | null;
|
|
158
158
|
locked: boolean;
|
|
@@ -234,7 +234,7 @@ export declare class LinearElementEditor {
|
|
|
234
234
|
offsetLeft: number;
|
|
235
235
|
fileHandle: import("browser-fs-access").FileSystemHandle | null;
|
|
236
236
|
collaborators: Map<import("../types").SocketId, Readonly<{
|
|
237
|
-
pointer?: import("../types").CollaboratorPointer | undefined;
|
|
237
|
+
pointer?: import("../types").CollaboratorPointer | undefined; /** indices */
|
|
238
238
|
button?: "up" | "down" | undefined;
|
|
239
239
|
selectedElementIds?: Readonly<{
|
|
240
240
|
[id: string]: true;
|
|
@@ -163,7 +163,8 @@ export class LinearElementEditor {
|
|
|
163
163
|
}
|
|
164
164
|
return false;
|
|
165
165
|
}
|
|
166
|
-
static handlePointerUp(event, editingLinearElement, appState,
|
|
166
|
+
static handlePointerUp(event, editingLinearElement, appState, app) {
|
|
167
|
+
const elementsMap = app.scene.getNonDeletedElementsMap();
|
|
167
168
|
const { elementId, selectedPointsIndices, isDragging, pointerDownState } = editingLinearElement;
|
|
168
169
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
|
169
170
|
if (!element) {
|
|
@@ -185,7 +186,7 @@ export class LinearElementEditor {
|
|
|
185
186
|
]);
|
|
186
187
|
}
|
|
187
188
|
const bindingElement = isBindingEnabled(appState)
|
|
188
|
-
? getHoveredElementForBinding(tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(element, selectedPoint, elementsMap)),
|
|
189
|
+
? getHoveredElementForBinding(tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(element, selectedPoint, elementsMap)), app)
|
|
189
190
|
: null;
|
|
190
191
|
bindings[selectedPoint === 0 ? "startBindingElement" : "endBindingElement"] = bindingElement;
|
|
191
192
|
}
|
|
@@ -313,7 +314,8 @@ export class LinearElementEditor {
|
|
|
313
314
|
}
|
|
314
315
|
return -1;
|
|
315
316
|
}
|
|
316
|
-
static handlePointerDown(event, appState, history, scenePointer, linearElementEditor,
|
|
317
|
+
static handlePointerDown(event, appState, history, scenePointer, linearElementEditor, app) {
|
|
318
|
+
const elementsMap = app.scene.getNonDeletedElementsMap();
|
|
317
319
|
const ret = {
|
|
318
320
|
didAddPoint: false,
|
|
319
321
|
hitElement: null,
|
|
@@ -357,7 +359,7 @@ export class LinearElementEditor {
|
|
|
357
359
|
},
|
|
358
360
|
selectedPointsIndices: [element.points.length - 1],
|
|
359
361
|
lastUncommittedPoint: null,
|
|
360
|
-
endBindingElement: getHoveredElementForBinding(scenePointer,
|
|
362
|
+
endBindingElement: getHoveredElementForBinding(scenePointer, app),
|
|
361
363
|
};
|
|
362
364
|
ret.didAddPoint = true;
|
|
363
365
|
return ret;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ExcalidrawElement, ExcalidrawImageElement, ExcalidrawTextElement, ExcalidrawLinearElement, ExcalidrawGenericElement, NonDeleted, TextAlign, GroupId, VerticalAlign, Arrowhead, ExcalidrawFreeDrawElement, FontFamilyValues, ExcalidrawTextContainer, ExcalidrawFrameElement, ExcalidrawEmbeddableElement, ExcalidrawMagicFrameElement, ExcalidrawIframeElement, ElementsMap } from "./types";
|
|
2
2
|
import { AppState } from "../types";
|
|
3
3
|
import { MarkOptional, Mutable } from "../utility-types";
|
|
4
|
-
export type ElementConstructorOpts = MarkOptional<Omit<ExcalidrawGenericElement, "id" | "type" | "isDeleted" | "updated">, "width" | "height" | "angle" | "groupIds" | "frameId" | "boundElements" | "seed" | "version" | "versionNonce" | "link" | "strokeStyle" | "fillStyle" | "strokeColor" | "backgroundColor" | "roughness" | "strokeWidth" | "roundness" | "locked" | "opacity" | "customData">;
|
|
4
|
+
export type ElementConstructorOpts = MarkOptional<Omit<ExcalidrawGenericElement, "id" | "type" | "isDeleted" | "updated">, "width" | "height" | "angle" | "groupIds" | "frameId" | "index" | "boundElements" | "seed" | "version" | "versionNonce" | "link" | "strokeStyle" | "fillStyle" | "strokeColor" | "backgroundColor" | "roughness" | "strokeWidth" | "roundness" | "locked" | "opacity" | "customData">;
|
|
5
5
|
export declare const newElement: (opts: {
|
|
6
6
|
type: ExcalidrawGenericElement["type"];
|
|
7
7
|
} & ElementConstructorOpts) => NonDeleted<ExcalidrawGenericElement>;
|
|
@@ -7,7 +7,7 @@ import { adjustXYWithRotation } from "../math";
|
|
|
7
7
|
import { getResizedElementAbsoluteCoords } from "./bounds";
|
|
8
8
|
import { measureText, normalizeText, wrapText, getBoundTextMaxWidth, getDefaultLineHeight, } from "./textElement";
|
|
9
9
|
import { DEFAULT_ELEMENT_PROPS, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_TEXT_ALIGN, DEFAULT_VERTICAL_ALIGN, VERTICAL_ALIGN, } from "../constants";
|
|
10
|
-
const _newElementBase = (type, { x, y, strokeColor = DEFAULT_ELEMENT_PROPS.strokeColor, backgroundColor = DEFAULT_ELEMENT_PROPS.backgroundColor, fillStyle = DEFAULT_ELEMENT_PROPS.fillStyle, strokeWidth = DEFAULT_ELEMENT_PROPS.strokeWidth, strokeStyle = DEFAULT_ELEMENT_PROPS.strokeStyle, roughness = DEFAULT_ELEMENT_PROPS.roughness, opacity = DEFAULT_ELEMENT_PROPS.opacity, width = 0, height = 0, angle = 0, groupIds = [], frameId = null, roundness = null, boundElements = null, link = null, locked = DEFAULT_ELEMENT_PROPS.locked, ...rest }) => {
|
|
10
|
+
const _newElementBase = (type, { x, y, strokeColor = DEFAULT_ELEMENT_PROPS.strokeColor, backgroundColor = DEFAULT_ELEMENT_PROPS.backgroundColor, fillStyle = DEFAULT_ELEMENT_PROPS.fillStyle, strokeWidth = DEFAULT_ELEMENT_PROPS.strokeWidth, strokeStyle = DEFAULT_ELEMENT_PROPS.strokeStyle, roughness = DEFAULT_ELEMENT_PROPS.roughness, opacity = DEFAULT_ELEMENT_PROPS.opacity, width = 0, height = 0, angle = 0, groupIds = [], frameId = null, index = null, roundness = null, boundElements = null, link = null, locked = DEFAULT_ELEMENT_PROPS.locked, ...rest }) => {
|
|
11
11
|
// assign type to guard against excess properties
|
|
12
12
|
const element = {
|
|
13
13
|
id: rest.id || randomId(),
|
|
@@ -26,6 +26,7 @@ const _newElementBase = (type, { x, y, strokeColor = DEFAULT_ELEMENT_PROPS.strok
|
|
|
26
26
|
opacity,
|
|
27
27
|
groupIds,
|
|
28
28
|
frameId,
|
|
29
|
+
index,
|
|
29
30
|
roundness,
|
|
30
31
|
seed: rest.seed ?? randomInteger(),
|
|
31
32
|
version: rest.version || 1,
|
|
@@ -61,7 +61,6 @@ export declare const getBoundTextElementPosition: (container: ExcalidrawElement,
|
|
|
61
61
|
} | undefined;
|
|
62
62
|
export declare const shouldAllowVerticalAlign: (selectedElements: NonDeletedExcalidrawElement[], elementsMap: ElementsMap) => boolean;
|
|
63
63
|
export declare const suppportsHorizontalAlign: (selectedElements: NonDeletedExcalidrawElement[], elementsMap: ElementsMap) => boolean;
|
|
64
|
-
export declare const getTextBindableContainerAtPosition: (elements: readonly ExcalidrawElement[], appState: AppState, x: number, y: number, elementsMap: ElementsMap) => ExcalidrawTextContainer | null;
|
|
65
64
|
declare const VALID_CONTAINER_TYPES: Set<string>;
|
|
66
65
|
export declare const isValidTextContainer: (element: {
|
|
67
66
|
type: ExcalidrawElementType;
|
|
@@ -4,10 +4,6 @@ import { ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, ARROW_LABEL_WIDTH_FRACTION, B
|
|
|
4
4
|
import { isTextElement } from ".";
|
|
5
5
|
import { isBoundToContainer, isArrowElement } from "./typeChecks";
|
|
6
6
|
import { LinearElementEditor } from "./linearElementEditor";
|
|
7
|
-
import { isTextBindableContainer } from "./typeChecks";
|
|
8
|
-
import { getElementAbsoluteCoords } from ".";
|
|
9
|
-
import { getSelectedElements } from "../scene";
|
|
10
|
-
import { isHittingElementNotConsideringBoundingBox } from "./collision";
|
|
11
7
|
import { resetOriginalContainerCache, updateOriginalContainerCache, } from "./containerCache";
|
|
12
8
|
export const normalizeText = (text) => {
|
|
13
9
|
return (normalizeEOL(text)
|
|
@@ -540,32 +536,6 @@ export const suppportsHorizontalAlign = (selectedElements, elementsMap) => {
|
|
|
540
536
|
return isTextElement(element);
|
|
541
537
|
});
|
|
542
538
|
};
|
|
543
|
-
export const getTextBindableContainerAtPosition = (elements, appState, x, y, elementsMap) => {
|
|
544
|
-
const selectedElements = getSelectedElements(elements, appState);
|
|
545
|
-
if (selectedElements.length === 1) {
|
|
546
|
-
return isTextBindableContainer(selectedElements[0], false)
|
|
547
|
-
? selectedElements[0]
|
|
548
|
-
: null;
|
|
549
|
-
}
|
|
550
|
-
let hitElement = null;
|
|
551
|
-
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
|
552
|
-
for (let index = elements.length - 1; index >= 0; --index) {
|
|
553
|
-
if (elements[index].isDeleted) {
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[index], elementsMap);
|
|
557
|
-
if (isArrowElement(elements[index]) &&
|
|
558
|
-
isHittingElementNotConsideringBoundingBox(elements[index], appState, null, [x, y], elementsMap)) {
|
|
559
|
-
hitElement = elements[index];
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
else if (x1 < x && x < x2 && y1 < y && y < y2) {
|
|
563
|
-
hitElement = elements[index];
|
|
564
|
-
break;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return isTextBindableContainer(hitElement, false) ? hitElement : null;
|
|
568
|
-
};
|
|
569
539
|
const VALID_CONTAINER_TYPES = new Set([
|
|
570
540
|
"rectangle",
|
|
571
541
|
"ellipse",
|
|
@@ -18,6 +18,9 @@ export type StrokeStyle = "solid" | "dashed" | "dotted";
|
|
|
18
18
|
export type TextAlign = typeof TEXT_ALIGN[keyof typeof TEXT_ALIGN];
|
|
19
19
|
type VerticalAlignKeys = keyof typeof VERTICAL_ALIGN;
|
|
20
20
|
export type VerticalAlign = typeof VERTICAL_ALIGN[VerticalAlignKeys];
|
|
21
|
+
export type FractionalIndex = string & {
|
|
22
|
+
_brand: "franctionalIndex";
|
|
23
|
+
};
|
|
21
24
|
type _ExcalidrawElementBase = Readonly<{
|
|
22
25
|
id: string;
|
|
23
26
|
x: number;
|
|
@@ -46,6 +49,11 @@ type _ExcalidrawElementBase = Readonly<{
|
|
|
46
49
|
Used for deterministic reconciliation of updates during collaboration,
|
|
47
50
|
in case the versions (see above) are identical. */
|
|
48
51
|
versionNonce: number;
|
|
52
|
+
/** String in a fractional form defined by https://github.com/rocicorp/fractional-indexing.
|
|
53
|
+
Used for ordering in multiplayer scenarios, such as during reconciliation or undo / redo.
|
|
54
|
+
Always kept in sync with the array order by `syncMovedIndices` and `syncInvalidIndices`.
|
|
55
|
+
Could be null, i.e. for new elements which were not yet assigned to the scene. */
|
|
56
|
+
index: FractionalIndex | null;
|
|
49
57
|
isDeleted: boolean;
|
|
50
58
|
/** List of groups the element belongs to.
|
|
51
59
|
Ordered from deepest to shallowest. */
|
|
@@ -90,6 +98,9 @@ export type IframeData = ({
|
|
|
90
98
|
h: number;
|
|
91
99
|
};
|
|
92
100
|
error?: Error;
|
|
101
|
+
sandbox?: {
|
|
102
|
+
allowSameOrigin?: boolean;
|
|
103
|
+
};
|
|
93
104
|
} & ({
|
|
94
105
|
type: "video" | "generic";
|
|
95
106
|
link: string;
|
|
@@ -125,6 +136,10 @@ export type ExcalidrawGenericElement = ExcalidrawSelectionElement | ExcalidrawRe
|
|
|
125
136
|
* between peers and contain no state local to the peer.
|
|
126
137
|
*/
|
|
127
138
|
export type ExcalidrawElement = ExcalidrawGenericElement | ExcalidrawTextElement | ExcalidrawLinearElement | ExcalidrawFreeDrawElement | ExcalidrawImageElement | ExcalidrawFrameElement | ExcalidrawMagicFrameElement | ExcalidrawIframeElement | ExcalidrawEmbeddableElement;
|
|
139
|
+
export type Ordered<TElement extends ExcalidrawElement> = TElement & {
|
|
140
|
+
index: FractionalIndex;
|
|
141
|
+
};
|
|
142
|
+
export type OrderedExcalidrawElement = Ordered<ExcalidrawElement>;
|
|
128
143
|
export type NonDeleted<TElement extends ExcalidrawElement> = TElement & {
|
|
129
144
|
isDeleted: boolean;
|
|
130
145
|
};
|
|
@@ -195,11 +210,11 @@ export type NonDeletedElementsMap = Map<ExcalidrawElement["id"], NonDeletedExcal
|
|
|
195
210
|
* Map of all excalidraw Scene elements, including deleted.
|
|
196
211
|
* Not a subset. Use this type when you need access to current Scene elements.
|
|
197
212
|
*/
|
|
198
|
-
export type SceneElementsMap = Map<ExcalidrawElement["id"], ExcalidrawElement
|
|
213
|
+
export type SceneElementsMap = Map<ExcalidrawElement["id"], Ordered<ExcalidrawElement>> & MakeBrand<"SceneElementsMap">;
|
|
199
214
|
/**
|
|
200
215
|
* Map of all non-deleted Scene elements.
|
|
201
216
|
* Not a subset. Use this type when you need access to current Scene elements.
|
|
202
217
|
*/
|
|
203
|
-
export type NonDeletedSceneElementsMap = Map<ExcalidrawElement["id"], NonDeletedExcalidrawElement
|
|
218
|
+
export type NonDeletedSceneElementsMap = Map<ExcalidrawElement["id"], Ordered<NonDeletedExcalidrawElement>> & MakeBrand<"NonDeletedSceneElementsMap">;
|
|
204
219
|
export type ElementsMapOrArray = readonly ExcalidrawElement[] | Readonly<ElementsMap>;
|
|
205
220
|
export {};
|
|
@@ -10,4 +10,7 @@ export declare class ImageSceneDataError extends Error {
|
|
|
10
10
|
code: ImageSceneDataErrorCode;
|
|
11
11
|
constructor(message?: string, code?: ImageSceneDataErrorCode);
|
|
12
12
|
}
|
|
13
|
+
export declare class InvalidFractionalIndexError extends Error {
|
|
14
|
+
code: "ELEMENT_HAS_INVALID_INDEX";
|
|
15
|
+
}
|
|
13
16
|
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ExcalidrawElement, OrderedExcalidrawElement } from "./element/types";
|
|
2
|
+
/**
|
|
3
|
+
* Envisioned relation between array order and fractional indices:
|
|
4
|
+
*
|
|
5
|
+
* 1) Array (or array-like ordered data structure) should be used as a cache of elements order, hiding the internal fractional indices implementation.
|
|
6
|
+
* - it's undesirable to to perform reorder for each related operation, thefeore it's necessary to cache the order defined by fractional indices into an ordered data structure
|
|
7
|
+
* - 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)
|
|
8
|
+
* - it's necessary to always keep the array support for backwards compatibility (restore) - old scenes, old libraries, supporting multiple excalidraw versions etc.
|
|
9
|
+
* - it's necessary to always keep the fractional indices in sync with the array order
|
|
10
|
+
* - elements with invalid indices should be detected and synced, without altering the already valid indices
|
|
11
|
+
*
|
|
12
|
+
* 2) Fractional indices should be used to reorder the elements, whenever the cached order is expected to be invalidated.
|
|
13
|
+
* - as the fractional indices are encoded as part of the elements, it opens up possibilties for incremental-like APIs
|
|
14
|
+
* - re-order based on fractional indices should be part of (multiplayer) operations such as reconcillitation & undo/redo
|
|
15
|
+
* - 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,
|
|
16
|
+
* as it's faster & more efficient to perform re-order based on array manipulation and later synchronisation of moved indices with the array order
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Ensure that all elements have valid fractional indices.
|
|
20
|
+
*
|
|
21
|
+
* @throws `InvalidFractionalIndexError` if invalid index is detected.
|
|
22
|
+
*/
|
|
23
|
+
export declare const validateFractionalIndices: (indices: (ExcalidrawElement["index"] | undefined)[]) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Order the elements based on the fractional indices.
|
|
26
|
+
* - when fractional indices are identical, break the tie based on the element id
|
|
27
|
+
* - when there is no fractional index in one of the elements, respect the order of the array
|
|
28
|
+
*/
|
|
29
|
+
export declare const orderByFractionalIndex: (elements: OrderedExcalidrawElement[]) => OrderedExcalidrawElement[];
|
|
30
|
+
/**
|
|
31
|
+
* Synchronizes invalid fractional indices of moved elements with the array order by mutating passed elements.
|
|
32
|
+
* If the synchronization fails or the result is invalid, it fallbacks to `syncInvalidIndices`.
|
|
33
|
+
*/
|
|
34
|
+
export declare const syncMovedIndices: (elements: readonly ExcalidrawElement[], movedElements: Map<string, ExcalidrawElement>) => OrderedExcalidrawElement[];
|
|
35
|
+
/**
|
|
36
|
+
* Synchronizes all invalid fractional indices with the array order by mutating passed elements.
|
|
37
|
+
*
|
|
38
|
+
* WARN: in edge cases it could modify the elements which were not moved, as it's impossible to guess the actually moved elements from the elements array itself.
|
|
39
|
+
*/
|
|
40
|
+
export declare const syncInvalidIndices: (elements: readonly ExcalidrawElement[]) => OrderedExcalidrawElement[];
|