@excalidraw/excalidraw 0.17.1-7441-4e2c539 → 0.17.1-a38e82f
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 +52 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js +20279 -0
- package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-2W5GQUR4.js → chunk-IM4WTX2M.js} +12 -6
- package/dist/browser/dev/excalidraw-assets-dev/chunk-IM4WTX2M.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js → en-IOBA4CS2.js} +4 -2
- package/dist/browser/dev/excalidraw-assets-dev/image-LK4UNFRZ.css +6 -0
- package/dist/browser/dev/excalidraw-assets-dev/image-LK4UNFRZ.css.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{image-HYNUJ3XL.js → image-VKDAL6BQ.js} +2 -4
- package/dist/browser/dev/excalidraw-assets-dev/roundRect-T5BX56ZF.js +161 -0
- package/dist/browser/dev/excalidraw-assets-dev/roundRect-T5BX56ZF.js.map +7 -0
- package/dist/browser/dev/index.css +189 -129
- package/dist/browser/dev/index.css.map +3 -3
- package/dist/browser/dev/index.js +34707 -26
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/chunk-LIG3S5TN.js +11 -0
- package/dist/browser/prod/excalidraw-assets/chunk-N2C5DK3B.js +55 -0
- package/dist/browser/prod/excalidraw-assets/en-WFZVQ7I6.js +1 -0
- package/dist/browser/prod/excalidraw-assets/image-4AT7LYMR.js +1 -0
- package/dist/browser/prod/excalidraw-assets/image-X66R2EM5.css +1 -0
- package/dist/browser/prod/excalidraw-assets/roundRect-2ACQK4DA.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +203 -1
- package/dist/{prod/en-RLIAOBCI.json → dev/en-TDNWCAOT.json} +9 -5
- package/dist/dev/index.css +189 -129
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +38445 -39402
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionAddToLibrary.d.ts +12 -12
- package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
- package/dist/excalidraw/actions/actionAlign.js +2 -1
- package/dist/excalidraw/actions/actionBoundText.d.ts +8 -8
- package/dist/excalidraw/actions/actionBoundText.js +8 -8
- package/dist/excalidraw/actions/actionCanvas.d.ts +46 -46
- package/dist/excalidraw/actions/actionClipboard.d.ts +27 -27
- package/dist/excalidraw/actions/actionClipboard.js +9 -2
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +12 -12
- package/dist/excalidraw/actions/actionDeleteSelected.js +3 -2
- package/dist/excalidraw/actions/actionDistribute.d.ts +2 -2
- package/dist/excalidraw/actions/actionDistribute.js +1 -1
- package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +1 -1
- package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -3
- package/dist/excalidraw/actions/actionElementLock.d.ts +8 -8
- package/dist/excalidraw/actions/actionExport.d.ts +35 -35
- package/dist/excalidraw/actions/actionExport.js +4 -4
- package/dist/excalidraw/actions/actionFinalize.d.ts +7 -7
- package/dist/excalidraw/actions/actionFinalize.js +7 -6
- package/dist/excalidraw/actions/actionFlip.d.ts +2 -2
- package/dist/excalidraw/actions/actionFlip.js +11 -11
- package/dist/excalidraw/actions/actionFrame.d.ts +13 -13
- package/dist/excalidraw/actions/actionFrame.js +1 -1
- package/dist/excalidraw/actions/actionGroup.d.ts +8 -8
- package/dist/excalidraw/actions/actionGroup.js +3 -2
- package/dist/excalidraw/actions/actionLinearEditor.d.ts +4 -4
- package/dist/excalidraw/actions/actionLinearEditor.js +1 -1
- package/dist/excalidraw/{element/Hyperlink.d.ts → actions/actionLink.d.ts} +28 -50
- package/dist/excalidraw/actions/actionLink.js +40 -0
- package/dist/excalidraw/actions/actionMenu.d.ts +11 -11
- package/dist/excalidraw/actions/actionNavigate.d.ts +8 -8
- package/dist/excalidraw/actions/actionNavigate.js +1 -1
- package/dist/excalidraw/actions/actionProperties.d.ts +64 -64
- package/dist/excalidraw/actions/actionProperties.js +32 -27
- package/dist/excalidraw/actions/actionSelectAll.d.ts +4 -4
- package/dist/excalidraw/actions/actionSelectAll.js +1 -1
- package/dist/excalidraw/actions/actionStyles.d.ts +6 -6
- package/dist/excalidraw/actions/actionStyles.js +4 -4
- package/dist/excalidraw/actions/actionToggleGridMode.d.ts +4 -4
- package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +4 -4
- package/dist/excalidraw/actions/actionToggleStats.d.ts +4 -4
- package/dist/excalidraw/actions/actionToggleViewMode.d.ts +4 -4
- package/dist/excalidraw/actions/actionToggleZenMode.d.ts +4 -4
- package/dist/excalidraw/actions/index.d.ts +1 -1
- package/dist/excalidraw/actions/index.js +1 -1
- package/dist/excalidraw/actions/manager.js +2 -1
- package/dist/excalidraw/align.d.ts +2 -2
- package/dist/excalidraw/align.js +2 -2
- package/dist/excalidraw/animated-trail.d.ts +33 -0
- package/dist/excalidraw/animated-trail.js +96 -0
- package/dist/excalidraw/animation-frame-handler.d.ts +16 -0
- package/dist/excalidraw/animation-frame-handler.js +55 -0
- package/dist/excalidraw/appState.d.ts +1 -1
- package/dist/excalidraw/appState.js +1 -3
- package/dist/excalidraw/clipboard.js +5 -5
- package/dist/excalidraw/components/Actions.d.ts +3 -3
- package/dist/excalidraw/components/Actions.js +18 -7
- package/dist/excalidraw/components/App.d.ts +32 -17
- package/dist/excalidraw/components/App.js +474 -339
- package/dist/excalidraw/components/Button.d.ts +1 -1
- package/dist/excalidraw/components/FilledButton.d.ts +2 -2
- package/dist/excalidraw/components/FilledButton.js +27 -3
- package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
- package/dist/excalidraw/components/ImageExportDialog.d.ts +2 -1
- package/dist/excalidraw/components/ImageExportDialog.js +16 -12
- package/dist/excalidraw/components/JSONExportDialog.js +1 -1
- package/dist/excalidraw/components/{LaserTool/LaserPointerButton.d.ts → LaserPointerButton.d.ts} +1 -1
- package/dist/excalidraw/components/{LaserTool/LaserPointerButton.js → LaserPointerButton.js} +2 -2
- package/dist/excalidraw/components/LayerUI.js +3 -3
- package/dist/excalidraw/components/MobileMenu.js +1 -1
- package/dist/excalidraw/components/ProjectName.d.ts +0 -1
- package/dist/excalidraw/components/ProjectName.js +1 -1
- package/dist/excalidraw/components/SVGLayer.d.ts +8 -0
- package/dist/excalidraw/components/SVGLayer.js +20 -0
- package/dist/excalidraw/components/ShareableLinkDialog.js +10 -10
- package/dist/excalidraw/components/Stack.d.ts +2 -2
- package/dist/excalidraw/components/TTDDialog/common.js +10 -1
- package/dist/excalidraw/components/TextField.d.ts +5 -2
- package/dist/excalidraw/components/TextField.js +6 -3
- package/dist/excalidraw/components/Toast.d.ts +3 -2
- package/dist/excalidraw/components/Toast.js +2 -2
- package/dist/excalidraw/components/ToolButton.js +2 -1
- package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +2 -2
- package/dist/excalidraw/components/canvases/InteractiveCanvas.js +6 -5
- package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +4 -3
- package/dist/excalidraw/components/canvases/StaticCanvas.js +7 -5
- package/dist/excalidraw/components/dropdownMenu/DropdownMenuContent.js +22 -2
- package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
- package/dist/excalidraw/components/hyperlink/Hyperlink.d.ts +19 -0
- package/dist/excalidraw/{element → components/hyperlink}/Hyperlink.js +40 -115
- package/dist/excalidraw/components/hyperlink/helpers.d.ts +7 -0
- package/dist/excalidraw/components/hyperlink/helpers.js +49 -0
- package/dist/excalidraw/components/icons.d.ts +2 -1
- package/dist/excalidraw/components/icons.js +2 -1
- package/dist/excalidraw/components/live-collaboration/LiveCollaborationTrigger.js +3 -2
- package/dist/excalidraw/components/main-menu/DefaultItems.js +5 -2
- package/dist/excalidraw/constants.d.ts +8 -0
- package/dist/excalidraw/constants.js +10 -0
- package/dist/excalidraw/data/blob.js +13 -14
- package/dist/excalidraw/data/filesystem.d.ts +1 -1
- package/dist/excalidraw/data/index.d.ts +2 -1
- package/dist/excalidraw/data/index.js +20 -16
- package/dist/excalidraw/data/json.d.ts +1 -1
- package/dist/excalidraw/data/json.js +5 -3
- package/dist/excalidraw/data/resave.d.ts +1 -1
- package/dist/excalidraw/data/resave.js +2 -2
- package/dist/excalidraw/data/restore.js +8 -13
- package/dist/excalidraw/data/transform.js +13 -9
- package/dist/excalidraw/distribute.d.ts +2 -2
- package/dist/excalidraw/distribute.js +2 -2
- package/dist/excalidraw/element/ElementCanvasButtons.d.ts +3 -2
- package/dist/excalidraw/element/ElementCanvasButtons.js +4 -4
- package/dist/excalidraw/element/binding.d.ts +9 -9
- package/dist/excalidraw/element/binding.js +61 -59
- package/dist/excalidraw/element/bounds.d.ts +5 -5
- package/dist/excalidraw/element/bounds.js +29 -32
- package/dist/excalidraw/element/collision.d.ts +11 -11
- package/dist/excalidraw/element/collision.js +49 -46
- package/dist/excalidraw/element/containerCache.d.ts +11 -0
- package/dist/excalidraw/element/containerCache.js +14 -0
- package/dist/excalidraw/element/dragElements.js +10 -19
- package/dist/excalidraw/element/embeddable.d.ts +11 -12
- package/dist/excalidraw/element/embeddable.js +17 -27
- package/dist/excalidraw/element/image.js +1 -2
- package/dist/excalidraw/element/index.d.ts +0 -1
- package/dist/excalidraw/element/index.js +0 -1
- package/dist/excalidraw/element/linearElementEditor.d.ts +35 -35
- package/dist/excalidraw/element/linearElementEditor.js +79 -80
- package/dist/excalidraw/element/newElement.d.ts +4 -6
- package/dist/excalidraw/element/newElement.js +11 -16
- package/dist/excalidraw/element/resizeElements.d.ts +6 -6
- package/dist/excalidraw/element/resizeElements.js +40 -46
- package/dist/excalidraw/element/resizeTest.d.ts +3 -3
- package/dist/excalidraw/element/resizeTest.js +4 -4
- package/dist/excalidraw/element/sizeHelpers.d.ts +2 -2
- package/dist/excalidraw/element/sizeHelpers.js +2 -2
- package/dist/excalidraw/element/textElement.d.ts +18 -20
- package/dist/excalidraw/element/textElement.js +80 -111
- package/dist/excalidraw/element/textWysiwyg.d.ts +1 -6
- package/dist/excalidraw/element/textWysiwyg.js +15 -37
- package/dist/excalidraw/element/transformHandles.d.ts +4 -4
- package/dist/excalidraw/element/transformHandles.js +6 -6
- package/dist/excalidraw/element/typeChecks.js +4 -1
- package/dist/excalidraw/element/types.d.ts +24 -11
- package/dist/excalidraw/emitter.d.ts +5 -9
- package/dist/excalidraw/emitter.js +12 -12
- package/dist/excalidraw/frame.d.ts +26 -20
- package/dist/excalidraw/frame.js +157 -84
- package/dist/excalidraw/groups.d.ts +3 -3
- package/dist/excalidraw/groups.js +11 -3
- package/dist/excalidraw/history.d.ts +1 -1
- package/dist/excalidraw/index.d.ts +7 -3
- package/dist/excalidraw/index.js +14 -5
- package/dist/excalidraw/laser-trails.d.ts +19 -0
- package/dist/excalidraw/laser-trails.js +95 -0
- package/dist/excalidraw/locales/en.json +9 -5
- package/dist/excalidraw/reactUtils.d.ts +14 -0
- package/dist/excalidraw/reactUtils.js +45 -0
- package/dist/excalidraw/renderer/helpers.d.ts +13 -0
- package/dist/excalidraw/renderer/helpers.js +39 -0
- package/dist/excalidraw/renderer/interactiveScene.d.ts +20 -0
- package/dist/excalidraw/renderer/{renderScene.js → interactiveScene.js} +199 -474
- package/dist/excalidraw/renderer/renderElement.d.ts +6 -6
- package/dist/excalidraw/renderer/renderElement.js +54 -366
- package/dist/excalidraw/renderer/staticScene.d.ts +11 -0
- package/dist/excalidraw/renderer/staticScene.js +205 -0
- package/dist/excalidraw/renderer/staticSvgScene.d.ts +5 -0
- package/dist/excalidraw/renderer/staticSvgScene.js +385 -0
- package/dist/excalidraw/scene/Fonts.js +2 -1
- package/dist/excalidraw/scene/Renderer.d.ts +1 -1
- package/dist/excalidraw/scene/Renderer.js +32 -20
- package/dist/excalidraw/scene/Scene.d.ts +10 -9
- package/dist/excalidraw/scene/Scene.js +45 -21
- package/dist/excalidraw/scene/Shape.d.ts +3 -1
- package/dist/excalidraw/scene/Shape.js +7 -5
- package/dist/excalidraw/scene/ShapeCache.d.ts +2 -1
- package/dist/excalidraw/scene/ShapeCache.js +1 -0
- package/dist/excalidraw/scene/comparisons.js +2 -1
- package/dist/excalidraw/scene/export.d.ts +3 -0
- package/dist/excalidraw/scene/export.js +20 -40
- package/dist/excalidraw/scene/index.d.ts +0 -1
- package/dist/excalidraw/scene/index.js +0 -1
- package/dist/excalidraw/scene/scrollbars.d.ts +1 -1
- package/dist/excalidraw/scene/scrollbars.js +1 -1
- package/dist/excalidraw/scene/selection.d.ts +5 -5
- package/dist/excalidraw/scene/selection.js +16 -14
- package/dist/excalidraw/scene/types.d.ts +11 -5
- package/dist/excalidraw/snapping.d.ts +7 -7
- package/dist/excalidraw/snapping.js +21 -20
- package/dist/excalidraw/types.d.ts +11 -12
- package/dist/excalidraw/utility-types.d.ts +5 -0
- package/dist/excalidraw/utils.d.ts +25 -16
- package/dist/excalidraw/utils.js +52 -45
- package/dist/{dev/en-RLIAOBCI.json → prod/en-TDNWCAOT.json} +9 -5
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +45 -45
- package/dist/utils/export.d.ts +0 -6
- package/dist/utils/export.js +0 -6
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/withinBounds.js +2 -1
- package/package.json +4 -4
- package/dist/browser/dev/excalidraw-assets-dev/chunk-2W5GQUR4.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-SUHLFFEF.js +0 -53449
- package/dist/browser/dev/excalidraw-assets-dev/chunk-SUHLFFEF.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/image-NOPDRTTM.css +0 -5797
- package/dist/browser/dev/excalidraw-assets-dev/image-NOPDRTTM.css.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-HE2P7BQ6.js +0 -257
- package/dist/browser/prod/excalidraw-assets/chunk-OWLL6VOG.js +0 -11
- package/dist/browser/prod/excalidraw-assets/en-ERQOR3OC.js +0 -1
- package/dist/browser/prod/excalidraw-assets/image-DZ6B4AID.js +0 -1
- package/dist/browser/prod/excalidraw-assets/image-J2QCCYAR.css +0 -1
- package/dist/excalidraw/components/LaserTool/LaserPathManager.d.ts +0 -28
- package/dist/excalidraw/components/LaserTool/LaserPathManager.js +0 -225
- package/dist/excalidraw/components/LaserTool/LaserTool.d.ts +0 -8
- package/dist/excalidraw/components/LaserTool/LaserTool.js +0 -15
- package/dist/excalidraw/renderer/renderScene.d.ts +0 -25
- package/dist/excalidraw/vite.config.d.mts +0 -2
- package/dist/excalidraw/vite.config.mjs +0 -13
- /package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js.map → en-IOBA4CS2.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-HYNUJ3XL.js.map → image-VKDAL6BQ.js.map} +0 -0
|
@@ -1,23 +1,38 @@
|
|
|
1
|
-
import oc from "open-color";
|
|
2
1
|
import { getElementAbsoluteCoords, OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, getTransformHandlesFromCoords, getTransformHandles, getCommonBounds, } from "../element";
|
|
3
|
-
import { roundRect } from "
|
|
2
|
+
import { roundRect } from "../renderer/roundRect";
|
|
4
3
|
import { getScrollBars, SCROLLBAR_COLOR, SCROLLBAR_WIDTH, } from "../scene/scrollbars";
|
|
5
|
-
import {
|
|
4
|
+
import { renderSelectionElement } from "../renderer/renderElement";
|
|
6
5
|
import { getClientColor } from "../clients";
|
|
7
|
-
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
8
6
|
import { isSelectedViaGroup, getSelectedGroupIds, getElementsInGroup, selectGroupsFromGivenElements, } from "../groups";
|
|
9
|
-
import { maxBindingGap } from "../element/collision";
|
|
10
7
|
import { OMIT_SIDES_FOR_FRAME, shouldShowBoundingBox, } from "../element/transformHandles";
|
|
11
|
-
import { throttleRAF } from "../utils";
|
|
8
|
+
import { arrayToMap, throttleRAF } from "../utils";
|
|
12
9
|
import { UserIdleState } from "../types";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import "
|
|
20
|
-
|
|
10
|
+
import { DEFAULT_TRANSFORM_HANDLE_SPACING, FRAME_STYLE } from "../constants";
|
|
11
|
+
import { renderSnaps } from "../renderer/renderSnaps";
|
|
12
|
+
import { maxBindingGap } from "../element/collision";
|
|
13
|
+
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
14
|
+
import { bootstrapCanvas, fillCircle, getNormalizedCanvasDimensions, } from "./helpers";
|
|
15
|
+
import oc from "open-color";
|
|
16
|
+
import { isFrameLikeElement, isLinearElement } from "../element/typeChecks";
|
|
17
|
+
const renderLinearElementPointHighlight = (context, appState, elementsMap) => {
|
|
18
|
+
const { elementId, hoverPointIndex } = appState.selectedLinearElement;
|
|
19
|
+
if (appState.editingLinearElement?.selectedPointsIndices?.includes(hoverPointIndex)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
|
23
|
+
if (!element) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const point = LinearElementEditor.getPointAtIndexGlobalCoordinates(element, hoverPointIndex, elementsMap);
|
|
27
|
+
context.save();
|
|
28
|
+
context.translate(appState.scrollX, appState.scrollY);
|
|
29
|
+
highlightPoint(point, context, appState);
|
|
30
|
+
context.restore();
|
|
31
|
+
};
|
|
32
|
+
const highlightPoint = (point, context, appState) => {
|
|
33
|
+
context.fillStyle = "rgba(105, 101, 219, 0.4)";
|
|
34
|
+
fillCircle(context, point[0], point[1], LinearElementEditor.POINT_HANDLE_SIZE / appState.zoom.value, false);
|
|
35
|
+
};
|
|
21
36
|
const strokeRectWithRotation = (context, x, y, width, height, cx, cy, angle, fill = false,
|
|
22
37
|
/** should account for zoom */
|
|
23
38
|
radius = 0) => {
|
|
@@ -51,53 +66,6 @@ const strokeDiamondWithRotation = (context, width, height, cx, cy, angle) => {
|
|
|
51
66
|
context.stroke();
|
|
52
67
|
context.restore();
|
|
53
68
|
};
|
|
54
|
-
const strokeEllipseWithRotation = (context, width, height, cx, cy, angle) => {
|
|
55
|
-
context.beginPath();
|
|
56
|
-
context.ellipse(cx, cy, width / 2, height / 2, angle, 0, Math.PI * 2);
|
|
57
|
-
context.stroke();
|
|
58
|
-
};
|
|
59
|
-
const fillCircle = (context, cx, cy, radius, stroke = true) => {
|
|
60
|
-
context.beginPath();
|
|
61
|
-
context.arc(cx, cy, radius, 0, Math.PI * 2);
|
|
62
|
-
context.fill();
|
|
63
|
-
if (stroke) {
|
|
64
|
-
context.stroke();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
const strokeGrid = (context, gridSize, scrollX, scrollY, zoom, width, height) => {
|
|
68
|
-
const BOLD_LINE_FREQUENCY = 5;
|
|
69
|
-
let GridLineColor;
|
|
70
|
-
(function (GridLineColor) {
|
|
71
|
-
GridLineColor["Bold"] = "#cccccc";
|
|
72
|
-
GridLineColor["Regular"] = "#e5e5e5";
|
|
73
|
-
})(GridLineColor || (GridLineColor = {}));
|
|
74
|
-
const offsetX = -Math.round(zoom.value / gridSize) * gridSize + (scrollX % gridSize);
|
|
75
|
-
const offsetY = -Math.round(zoom.value / gridSize) * gridSize + (scrollY % gridSize);
|
|
76
|
-
const lineWidth = Math.min(1 / zoom.value, 1);
|
|
77
|
-
const spaceWidth = 1 / zoom.value;
|
|
78
|
-
const lineDash = [lineWidth * 3, spaceWidth + (lineWidth + spaceWidth)];
|
|
79
|
-
context.save();
|
|
80
|
-
context.lineWidth = lineWidth;
|
|
81
|
-
for (let x = offsetX; x < offsetX + width + gridSize * 2; x += gridSize) {
|
|
82
|
-
const isBold = Math.round(x - scrollX) % (BOLD_LINE_FREQUENCY * gridSize) === 0;
|
|
83
|
-
context.beginPath();
|
|
84
|
-
context.setLineDash(isBold ? [] : lineDash);
|
|
85
|
-
context.strokeStyle = isBold ? GridLineColor.Bold : GridLineColor.Regular;
|
|
86
|
-
context.moveTo(x, offsetY - gridSize);
|
|
87
|
-
context.lineTo(x, offsetY + height + gridSize * 2);
|
|
88
|
-
context.stroke();
|
|
89
|
-
}
|
|
90
|
-
for (let y = offsetY; y < offsetY + height + gridSize * 2; y += gridSize) {
|
|
91
|
-
const isBold = Math.round(y - scrollY) % (BOLD_LINE_FREQUENCY * gridSize) === 0;
|
|
92
|
-
context.beginPath();
|
|
93
|
-
context.setLineDash(isBold ? [] : lineDash);
|
|
94
|
-
context.strokeStyle = isBold ? GridLineColor.Bold : GridLineColor.Regular;
|
|
95
|
-
context.moveTo(offsetX - gridSize, y);
|
|
96
|
-
context.lineTo(offsetX + width + gridSize * 2, y);
|
|
97
|
-
context.stroke();
|
|
98
|
-
}
|
|
99
|
-
context.restore();
|
|
100
|
-
};
|
|
101
69
|
const renderSingleLinearPoint = (context, appState, point, radius, isSelected, isPhantomPoint = false) => {
|
|
102
70
|
context.strokeStyle = "#5e5ad8";
|
|
103
71
|
context.setLineDash([]);
|
|
@@ -110,14 +78,134 @@ const renderSingleLinearPoint = (context, appState, point, radius, isSelected, i
|
|
|
110
78
|
}
|
|
111
79
|
fillCircle(context, point[0], point[1], radius / appState.zoom.value, !isPhantomPoint);
|
|
112
80
|
};
|
|
113
|
-
const
|
|
81
|
+
const strokeEllipseWithRotation = (context, width, height, cx, cy, angle) => {
|
|
82
|
+
context.beginPath();
|
|
83
|
+
context.ellipse(cx, cy, width / 2, height / 2, angle, 0, Math.PI * 2);
|
|
84
|
+
context.stroke();
|
|
85
|
+
};
|
|
86
|
+
const renderBindingHighlightForBindableElement = (context, element, elementsMap) => {
|
|
87
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
88
|
+
const width = x2 - x1;
|
|
89
|
+
const height = y2 - y1;
|
|
90
|
+
const threshold = maxBindingGap(element, width, height);
|
|
91
|
+
// So that we don't overlap the element itself
|
|
92
|
+
const strokeOffset = 4;
|
|
93
|
+
context.strokeStyle = "rgba(0,0,0,.05)";
|
|
94
|
+
context.lineWidth = threshold - strokeOffset;
|
|
95
|
+
const padding = strokeOffset / 2 + threshold / 2;
|
|
96
|
+
switch (element.type) {
|
|
97
|
+
case "rectangle":
|
|
98
|
+
case "text":
|
|
99
|
+
case "image":
|
|
100
|
+
case "iframe":
|
|
101
|
+
case "embeddable":
|
|
102
|
+
case "frame":
|
|
103
|
+
case "magicframe":
|
|
104
|
+
strokeRectWithRotation(context, x1 - padding, y1 - padding, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
105
|
+
break;
|
|
106
|
+
case "diamond":
|
|
107
|
+
const side = Math.hypot(width, height);
|
|
108
|
+
const wPadding = (padding * side) / height;
|
|
109
|
+
const hPadding = (padding * side) / width;
|
|
110
|
+
strokeDiamondWithRotation(context, width + wPadding * 2, height + hPadding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
111
|
+
break;
|
|
112
|
+
case "ellipse":
|
|
113
|
+
strokeEllipseWithRotation(context, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const renderBindingHighlightForSuggestedPointBinding = (context, suggestedBinding, elementsMap) => {
|
|
118
|
+
const [element, startOrEnd, bindableElement] = suggestedBinding;
|
|
119
|
+
const threshold = maxBindingGap(bindableElement, bindableElement.width, bindableElement.height);
|
|
120
|
+
context.strokeStyle = "rgba(0,0,0,0)";
|
|
121
|
+
context.fillStyle = "rgba(0,0,0,.05)";
|
|
122
|
+
const pointIndices = startOrEnd === "both" ? [0, -1] : startOrEnd === "start" ? [0] : [-1];
|
|
123
|
+
pointIndices.forEach((index) => {
|
|
124
|
+
const [x, y] = LinearElementEditor.getPointAtIndexGlobalCoordinates(element, index, elementsMap);
|
|
125
|
+
fillCircle(context, x, y, threshold);
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
const renderSelectionBorder = (context, appState, elementProperties, padding = DEFAULT_TRANSFORM_HANDLE_SPACING * 2) => {
|
|
129
|
+
const { angle, elementX1, elementY1, elementX2, elementY2, selectionColors, cx, cy, dashed, activeEmbeddable, } = elementProperties;
|
|
130
|
+
const elementWidth = elementX2 - elementX1;
|
|
131
|
+
const elementHeight = elementY2 - elementY1;
|
|
132
|
+
const linePadding = padding / appState.zoom.value;
|
|
133
|
+
const lineWidth = 8 / appState.zoom.value;
|
|
134
|
+
const spaceWidth = 4 / appState.zoom.value;
|
|
135
|
+
context.save();
|
|
136
|
+
context.translate(appState.scrollX, appState.scrollY);
|
|
137
|
+
context.lineWidth = (activeEmbeddable ? 4 : 1) / appState.zoom.value;
|
|
138
|
+
const count = selectionColors.length;
|
|
139
|
+
for (let index = 0; index < count; ++index) {
|
|
140
|
+
context.strokeStyle = selectionColors[index];
|
|
141
|
+
if (dashed) {
|
|
142
|
+
context.setLineDash([
|
|
143
|
+
lineWidth,
|
|
144
|
+
spaceWidth + (lineWidth + spaceWidth) * (count - 1),
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
context.lineDashOffset = (lineWidth + spaceWidth) * index;
|
|
148
|
+
strokeRectWithRotation(context, elementX1 - linePadding, elementY1 - linePadding, elementWidth + linePadding * 2, elementHeight + linePadding * 2, cx, cy, angle);
|
|
149
|
+
}
|
|
150
|
+
context.restore();
|
|
151
|
+
};
|
|
152
|
+
const renderBindingHighlight = (context, appState, suggestedBinding, elementsMap) => {
|
|
153
|
+
const renderHighlight = Array.isArray(suggestedBinding)
|
|
154
|
+
? renderBindingHighlightForSuggestedPointBinding
|
|
155
|
+
: renderBindingHighlightForBindableElement;
|
|
156
|
+
context.save();
|
|
157
|
+
context.translate(appState.scrollX, appState.scrollY);
|
|
158
|
+
renderHighlight(context, suggestedBinding, elementsMap);
|
|
159
|
+
context.restore();
|
|
160
|
+
};
|
|
161
|
+
const renderFrameHighlight = (context, appState, frame, elementsMap) => {
|
|
162
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(frame, elementsMap);
|
|
163
|
+
const width = x2 - x1;
|
|
164
|
+
const height = y2 - y1;
|
|
165
|
+
context.strokeStyle = "rgb(0,118,255)";
|
|
166
|
+
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
|
167
|
+
context.save();
|
|
168
|
+
context.translate(appState.scrollX, appState.scrollY);
|
|
169
|
+
strokeRectWithRotation(context, x1, y1, width, height, x1 + width / 2, y1 + height / 2, frame.angle, false, FRAME_STYLE.radius / appState.zoom.value);
|
|
170
|
+
context.restore();
|
|
171
|
+
};
|
|
172
|
+
const renderElementsBoxHighlight = (context, appState, elements) => {
|
|
173
|
+
const individualElements = elements.filter((element) => element.groupIds.length === 0);
|
|
174
|
+
const elementsInGroups = elements.filter((element) => element.groupIds.length > 0);
|
|
175
|
+
const getSelectionFromElements = (elements) => {
|
|
176
|
+
const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(elements);
|
|
177
|
+
return {
|
|
178
|
+
angle: 0,
|
|
179
|
+
elementX1,
|
|
180
|
+
elementX2,
|
|
181
|
+
elementY1,
|
|
182
|
+
elementY2,
|
|
183
|
+
selectionColors: ["rgb(0,118,255)"],
|
|
184
|
+
dashed: false,
|
|
185
|
+
cx: elementX1 + (elementX2 - elementX1) / 2,
|
|
186
|
+
cy: elementY1 + (elementY2 - elementY1) / 2,
|
|
187
|
+
activeEmbeddable: false,
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
const getSelectionForGroupId = (groupId) => {
|
|
191
|
+
const groupElements = getElementsInGroup(elements, groupId);
|
|
192
|
+
return getSelectionFromElements(groupElements);
|
|
193
|
+
};
|
|
194
|
+
Object.entries(selectGroupsFromGivenElements(elementsInGroups, appState))
|
|
195
|
+
.filter(([id, isSelected]) => isSelected)
|
|
196
|
+
.map(([id, isSelected]) => id)
|
|
197
|
+
.map((groupId) => getSelectionForGroupId(groupId))
|
|
198
|
+
.concat(individualElements.map((element) => getSelectionFromElements([element])))
|
|
199
|
+
.forEach((selection) => renderSelectionBorder(context, appState, selection));
|
|
200
|
+
};
|
|
201
|
+
const renderLinearPointHandles = (context, appState, element, elementsMap) => {
|
|
114
202
|
if (!appState.selectedLinearElement) {
|
|
115
203
|
return;
|
|
116
204
|
}
|
|
117
205
|
context.save();
|
|
118
206
|
context.translate(appState.scrollX, appState.scrollY);
|
|
119
207
|
context.lineWidth = 1 / appState.zoom.value;
|
|
120
|
-
const points = LinearElementEditor.getPointsGlobalCoordinates(element);
|
|
208
|
+
const points = LinearElementEditor.getPointsGlobalCoordinates(element, elementsMap);
|
|
121
209
|
const { POINT_HANDLE_SIZE } = LinearElementEditor;
|
|
122
210
|
const radius = appState.editingLinearElement
|
|
123
211
|
? POINT_HANDLE_SIZE
|
|
@@ -127,7 +215,7 @@ const renderLinearPointHandles = (context, appState, element) => {
|
|
|
127
215
|
renderSingleLinearPoint(context, appState, point, radius, isSelected);
|
|
128
216
|
});
|
|
129
217
|
//Rendering segment mid points
|
|
130
|
-
const midPoints = LinearElementEditor.getEditorMidPoints(element, appState).filter((midPoint) => midPoint !== null);
|
|
218
|
+
const midPoints = LinearElementEditor.getEditorMidPoints(element, elementsMap, appState).filter((midPoint) => midPoint !== null);
|
|
131
219
|
midPoints.forEach((segmentMidPoint) => {
|
|
132
220
|
if (appState?.selectedLinearElement?.segmentMidPointHoveredCoords &&
|
|
133
221
|
LinearElementEditor.arePointsEqual(segmentMidPoint, appState.selectedLinearElement.segmentMidPointHoveredCoords)) {
|
|
@@ -150,70 +238,36 @@ const renderLinearPointHandles = (context, appState, element) => {
|
|
|
150
238
|
});
|
|
151
239
|
context.restore();
|
|
152
240
|
};
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
context.rect(0, 0, frame.width, frame.height);
|
|
180
|
-
}
|
|
181
|
-
context.clip();
|
|
182
|
-
context.translate(-(frame.x + appState.scrollX), -(frame.y + appState.scrollY));
|
|
183
|
-
};
|
|
184
|
-
const getNormalizedCanvasDimensions = (canvas, scale) => {
|
|
185
|
-
// When doing calculations based on canvas width we should used normalized one
|
|
186
|
-
return [canvas.width / scale, canvas.height / scale];
|
|
187
|
-
};
|
|
188
|
-
const bootstrapCanvas = ({ canvas, scale, normalizedWidth, normalizedHeight, theme, isExporting, viewBackgroundColor, }) => {
|
|
189
|
-
const context = canvas.getContext("2d");
|
|
190
|
-
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
191
|
-
context.scale(scale, scale);
|
|
192
|
-
if (isExporting && theme === "dark") {
|
|
193
|
-
context.filter = THEME_FILTER;
|
|
194
|
-
}
|
|
195
|
-
// Paint background
|
|
196
|
-
if (typeof viewBackgroundColor === "string") {
|
|
197
|
-
const hasTransparence = viewBackgroundColor === "transparent" ||
|
|
198
|
-
viewBackgroundColor.length === 5 || // #RGBA
|
|
199
|
-
viewBackgroundColor.length === 9 || // #RRGGBBA
|
|
200
|
-
/(hsla|rgba)\(/.test(viewBackgroundColor);
|
|
201
|
-
if (hasTransparence) {
|
|
202
|
-
context.clearRect(0, 0, normalizedWidth, normalizedHeight);
|
|
241
|
+
const renderTransformHandles = (context, renderConfig, appState, transformHandles, angle) => {
|
|
242
|
+
Object.keys(transformHandles).forEach((key) => {
|
|
243
|
+
const transformHandle = transformHandles[key];
|
|
244
|
+
if (transformHandle !== undefined) {
|
|
245
|
+
const [x, y, width, height] = transformHandle;
|
|
246
|
+
context.save();
|
|
247
|
+
context.lineWidth = 1 / appState.zoom.value;
|
|
248
|
+
if (renderConfig.selectionColor) {
|
|
249
|
+
context.strokeStyle = renderConfig.selectionColor;
|
|
250
|
+
}
|
|
251
|
+
if (key === "rotation") {
|
|
252
|
+
fillCircle(context, x + width / 2, y + height / 2, width / 2);
|
|
253
|
+
// prefer round corners if roundRect API is available
|
|
254
|
+
}
|
|
255
|
+
else if (context.roundRect) {
|
|
256
|
+
context.beginPath();
|
|
257
|
+
context.roundRect(x, y, width, height, 2 / appState.zoom.value);
|
|
258
|
+
context.fill();
|
|
259
|
+
context.stroke();
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
strokeRectWithRotation(context, x, y, width, height, x + width / 2, y + height / 2, angle, true);
|
|
263
|
+
}
|
|
264
|
+
context.restore();
|
|
203
265
|
}
|
|
204
|
-
|
|
205
|
-
context.fillStyle = viewBackgroundColor;
|
|
206
|
-
context.fillRect(0, 0, normalizedWidth, normalizedHeight);
|
|
207
|
-
context.restore();
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
context.clearRect(0, 0, normalizedWidth, normalizedHeight);
|
|
211
|
-
}
|
|
212
|
-
return context;
|
|
266
|
+
});
|
|
213
267
|
};
|
|
214
|
-
const _renderInteractiveScene = ({ canvas,
|
|
268
|
+
const _renderInteractiveScene = ({ canvas, elementsMap, visibleElements, selectedElements, scale, appState, renderConfig, }) => {
|
|
215
269
|
if (canvas === null) {
|
|
216
|
-
return { atLeastOneVisibleElement: false,
|
|
270
|
+
return { atLeastOneVisibleElement: false, elementsMap };
|
|
217
271
|
}
|
|
218
272
|
const [normalizedWidth, normalizedHeight] = getNormalizedCanvasDimensions(canvas, scale);
|
|
219
273
|
const context = bootstrapCanvas({
|
|
@@ -237,7 +291,7 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
237
291
|
}
|
|
238
292
|
});
|
|
239
293
|
if (editingLinearElement) {
|
|
240
|
-
renderLinearPointHandles(context, appState, editingLinearElement);
|
|
294
|
+
renderLinearPointHandles(context, appState, editingLinearElement, elementsMap);
|
|
241
295
|
}
|
|
242
296
|
// Paint selection element
|
|
243
297
|
if (appState.selectionElement) {
|
|
@@ -252,11 +306,11 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
252
306
|
appState.suggestedBindings
|
|
253
307
|
.filter((binding) => binding != null)
|
|
254
308
|
.forEach((suggestedBinding) => {
|
|
255
|
-
renderBindingHighlight(context, appState, suggestedBinding);
|
|
309
|
+
renderBindingHighlight(context, appState, suggestedBinding, elementsMap);
|
|
256
310
|
});
|
|
257
311
|
}
|
|
258
312
|
if (appState.frameToHighlight) {
|
|
259
|
-
renderFrameHighlight(context, appState, appState.frameToHighlight);
|
|
313
|
+
renderFrameHighlight(context, appState, appState.frameToHighlight, elementsMap);
|
|
260
314
|
}
|
|
261
315
|
if (appState.elementsToHighlight) {
|
|
262
316
|
renderElementsBoxHighlight(context, appState, appState.elementsToHighlight);
|
|
@@ -267,11 +321,11 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
267
321
|
// correct element from visible elements
|
|
268
322
|
if (selectedElements.length === 1 &&
|
|
269
323
|
appState.editingLinearElement?.elementId === selectedElements[0].id) {
|
|
270
|
-
renderLinearPointHandles(context, appState, selectedElements[0]);
|
|
324
|
+
renderLinearPointHandles(context, appState, selectedElements[0], elementsMap);
|
|
271
325
|
}
|
|
272
326
|
if (appState.selectedLinearElement &&
|
|
273
327
|
appState.selectedLinearElement.hoverPointIndex >= 0) {
|
|
274
|
-
renderLinearElementPointHighlight(context, appState);
|
|
328
|
+
renderLinearElementPointHighlight(context, appState, elementsMap);
|
|
275
329
|
}
|
|
276
330
|
// Paint selected elements
|
|
277
331
|
if (!appState.multiElement && !appState.editingLinearElement) {
|
|
@@ -281,19 +335,17 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
281
335
|
if (isSingleLinearElementSelected &&
|
|
282
336
|
appState.selectedLinearElement?.elementId === selectedElements[0].id &&
|
|
283
337
|
!selectedElements[0].locked) {
|
|
284
|
-
renderLinearPointHandles(context, appState, selectedElements[0]);
|
|
338
|
+
renderLinearPointHandles(context, appState, selectedElements[0], elementsMap);
|
|
285
339
|
}
|
|
286
340
|
const selectionColor = renderConfig.selectionColor || oc.black;
|
|
287
341
|
if (showBoundingBox) {
|
|
288
342
|
// Optimisation for finding quickly relevant element ids
|
|
289
|
-
const locallySelectedIds = selectedElements
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}, {});
|
|
293
|
-
const selections = elements.reduce((acc, element) => {
|
|
343
|
+
const locallySelectedIds = arrayToMap(selectedElements);
|
|
344
|
+
const selections = [];
|
|
345
|
+
for (const element of elementsMap.values()) {
|
|
294
346
|
const selectionColors = [];
|
|
295
347
|
// local user
|
|
296
|
-
if (locallySelectedIds
|
|
348
|
+
if (locallySelectedIds.has(element.id) &&
|
|
297
349
|
!isSelectedViaGroup(appState, element)) {
|
|
298
350
|
selectionColors.push(selectionColor);
|
|
299
351
|
}
|
|
@@ -305,8 +357,8 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
305
357
|
}));
|
|
306
358
|
}
|
|
307
359
|
if (selectionColors.length) {
|
|
308
|
-
const [elementX1, elementY1, elementX2, elementY2, cx, cy] = getElementAbsoluteCoords(element, true);
|
|
309
|
-
|
|
360
|
+
const [elementX1, elementY1, elementX2, elementY2, cx, cy] = getElementAbsoluteCoords(element, elementsMap, true);
|
|
361
|
+
selections.push({
|
|
310
362
|
angle: element.angle,
|
|
311
363
|
elementX1,
|
|
312
364
|
elementY1,
|
|
@@ -320,10 +372,9 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
320
372
|
appState.activeEmbeddable.state === "active",
|
|
321
373
|
});
|
|
322
374
|
}
|
|
323
|
-
|
|
324
|
-
}, []);
|
|
375
|
+
}
|
|
325
376
|
const addSelectionForGroupId = (groupId) => {
|
|
326
|
-
const groupElements = getElementsInGroup(
|
|
377
|
+
const groupElements = getElementsInGroup(elementsMap, groupId);
|
|
327
378
|
const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(groupElements);
|
|
328
379
|
selections.push({
|
|
329
380
|
angle: 0,
|
|
@@ -352,13 +403,13 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
352
403
|
context.translate(appState.scrollX, appState.scrollY);
|
|
353
404
|
if (selectedElements.length === 1) {
|
|
354
405
|
context.fillStyle = oc.white;
|
|
355
|
-
const transformHandles = getTransformHandles(selectedElements[0], appState.zoom, "mouse");
|
|
406
|
+
const transformHandles = getTransformHandles(selectedElements[0], appState.zoom, elementsMap, "mouse");
|
|
356
407
|
if (!appState.viewModeEnabled && showBoundingBox) {
|
|
357
408
|
renderTransformHandles(context, renderConfig, appState, transformHandles, selectedElements[0].angle);
|
|
358
409
|
}
|
|
359
410
|
}
|
|
360
411
|
else if (selectedElements.length > 1 && !appState.isRotating) {
|
|
361
|
-
const dashedLinePadding = (
|
|
412
|
+
const dashedLinePadding = (DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / appState.zoom.value;
|
|
362
413
|
context.fillStyle = oc.white;
|
|
363
414
|
const [x1, y1, x2, y2] = getCommonBounds(selectedElements);
|
|
364
415
|
const initialLineDash = context.getLineDash();
|
|
@@ -496,7 +547,7 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
496
547
|
// Paint scrollbars
|
|
497
548
|
let scrollBars;
|
|
498
549
|
if (renderConfig.renderScrollbars) {
|
|
499
|
-
scrollBars = getScrollBars(
|
|
550
|
+
scrollBars = getScrollBars(visibleElements, normalizedWidth, normalizedHeight, appState);
|
|
500
551
|
context.save();
|
|
501
552
|
context.fillStyle = SCROLLBAR_COLOR;
|
|
502
553
|
context.strokeStyle = "rgba(255,255,255,0.8)";
|
|
@@ -510,119 +561,16 @@ const _renderInteractiveScene = ({ canvas, elements, visibleElements, selectedEl
|
|
|
510
561
|
return {
|
|
511
562
|
scrollBars,
|
|
512
563
|
atLeastOneVisibleElement: visibleElements.length > 0,
|
|
513
|
-
|
|
564
|
+
elementsMap,
|
|
514
565
|
};
|
|
515
566
|
};
|
|
516
|
-
const _renderStaticScene = ({ canvas, rc, elements, visibleElements, scale, appState, renderConfig, }) => {
|
|
517
|
-
if (canvas === null) {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
const { renderGrid = true, isExporting } = renderConfig;
|
|
521
|
-
const [normalizedWidth, normalizedHeight] = getNormalizedCanvasDimensions(canvas, scale);
|
|
522
|
-
const context = bootstrapCanvas({
|
|
523
|
-
canvas,
|
|
524
|
-
scale,
|
|
525
|
-
normalizedWidth,
|
|
526
|
-
normalizedHeight,
|
|
527
|
-
theme: appState.theme,
|
|
528
|
-
isExporting,
|
|
529
|
-
viewBackgroundColor: appState.viewBackgroundColor,
|
|
530
|
-
});
|
|
531
|
-
// Apply zoom
|
|
532
|
-
context.scale(appState.zoom.value, appState.zoom.value);
|
|
533
|
-
// Grid
|
|
534
|
-
if (renderGrid && appState.gridSize) {
|
|
535
|
-
strokeGrid(context, appState.gridSize, appState.scrollX, appState.scrollY, appState.zoom, normalizedWidth / appState.zoom.value, normalizedHeight / appState.zoom.value);
|
|
536
|
-
}
|
|
537
|
-
const groupsToBeAddedToFrame = new Set();
|
|
538
|
-
visibleElements.forEach((element) => {
|
|
539
|
-
if (element.groupIds.length > 0 &&
|
|
540
|
-
appState.frameToHighlight &&
|
|
541
|
-
appState.selectedElementIds[element.id] &&
|
|
542
|
-
(elementOverlapsWithFrame(element, appState.frameToHighlight) ||
|
|
543
|
-
element.groupIds.find((groupId) => groupsToBeAddedToFrame.has(groupId)))) {
|
|
544
|
-
element.groupIds.forEach((groupId) => groupsToBeAddedToFrame.add(groupId));
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
// Paint visible elements
|
|
548
|
-
visibleElements
|
|
549
|
-
.filter((el) => !isIframeLikeOrItsLabel(el))
|
|
550
|
-
.forEach((element) => {
|
|
551
|
-
try {
|
|
552
|
-
const frameId = element.frameId || appState.frameToHighlight?.id;
|
|
553
|
-
if (frameId &&
|
|
554
|
-
appState.frameRendering.enabled &&
|
|
555
|
-
appState.frameRendering.clip) {
|
|
556
|
-
context.save();
|
|
557
|
-
const frame = getTargetFrame(element, appState);
|
|
558
|
-
// TODO do we need to check isElementInFrame here?
|
|
559
|
-
if (frame && isElementInFrame(element, elements, appState)) {
|
|
560
|
-
frameClip(frame, context, renderConfig, appState);
|
|
561
|
-
}
|
|
562
|
-
renderElement(element, rc, context, renderConfig, appState);
|
|
563
|
-
context.restore();
|
|
564
|
-
}
|
|
565
|
-
else {
|
|
566
|
-
renderElement(element, rc, context, renderConfig, appState);
|
|
567
|
-
}
|
|
568
|
-
if (!isExporting) {
|
|
569
|
-
renderLinkIcon(element, context, appState);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
catch (error) {
|
|
573
|
-
console.error(error);
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
// render embeddables on top
|
|
577
|
-
visibleElements
|
|
578
|
-
.filter((el) => isIframeLikeOrItsLabel(el))
|
|
579
|
-
.forEach((element) => {
|
|
580
|
-
try {
|
|
581
|
-
const render = () => {
|
|
582
|
-
renderElement(element, rc, context, renderConfig, appState);
|
|
583
|
-
if (isIframeLikeElement(element) &&
|
|
584
|
-
(isExporting ||
|
|
585
|
-
(isEmbeddableElement(element) && !element.validated)) &&
|
|
586
|
-
element.width &&
|
|
587
|
-
element.height) {
|
|
588
|
-
const label = createPlaceholderEmbeddableLabel(element);
|
|
589
|
-
renderElement(label, rc, context, renderConfig, appState);
|
|
590
|
-
}
|
|
591
|
-
if (!isExporting) {
|
|
592
|
-
renderLinkIcon(element, context, appState);
|
|
593
|
-
}
|
|
594
|
-
};
|
|
595
|
-
// - when exporting the whole canvas, we DO NOT apply clipping
|
|
596
|
-
// - when we are exporting a particular frame, apply clipping
|
|
597
|
-
// if the containing frame is not selected, apply clipping
|
|
598
|
-
const frameId = element.frameId || appState.frameToHighlight?.id;
|
|
599
|
-
if (frameId &&
|
|
600
|
-
appState.frameRendering.enabled &&
|
|
601
|
-
appState.frameRendering.clip) {
|
|
602
|
-
context.save();
|
|
603
|
-
const frame = getTargetFrame(element, appState);
|
|
604
|
-
if (frame && isElementInFrame(element, elements, appState)) {
|
|
605
|
-
frameClip(frame, context, renderConfig, appState);
|
|
606
|
-
}
|
|
607
|
-
render();
|
|
608
|
-
context.restore();
|
|
609
|
-
}
|
|
610
|
-
else {
|
|
611
|
-
render();
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
catch (error) {
|
|
615
|
-
console.error(error);
|
|
616
|
-
}
|
|
617
|
-
});
|
|
618
|
-
};
|
|
619
567
|
/** throttled to animation framerate */
|
|
620
|
-
const renderInteractiveSceneThrottled = throttleRAF((config) => {
|
|
568
|
+
export const renderInteractiveSceneThrottled = throttleRAF((config) => {
|
|
621
569
|
const ret = _renderInteractiveScene(config);
|
|
622
570
|
config.callback?.(ret);
|
|
623
571
|
}, { trailing: true });
|
|
624
572
|
/**
|
|
625
|
-
* Interactive scene is the ui-canvas where we render
|
|
573
|
+
* Interactive scene is the ui-canvas where we render bounding boxes, selections
|
|
626
574
|
* and other ui stuff.
|
|
627
575
|
*/
|
|
628
576
|
export const renderInteractiveScene = (renderConfig, throttle) => {
|
|
@@ -634,226 +582,3 @@ export const renderInteractiveScene = (renderConfig, throttle) => {
|
|
|
634
582
|
renderConfig.callback(ret);
|
|
635
583
|
return ret;
|
|
636
584
|
};
|
|
637
|
-
/** throttled to animation framerate */
|
|
638
|
-
const renderStaticSceneThrottled = throttleRAF((config) => {
|
|
639
|
-
_renderStaticScene(config);
|
|
640
|
-
}, { trailing: true });
|
|
641
|
-
/**
|
|
642
|
-
* Static scene is the non-ui canvas where we render elements.
|
|
643
|
-
*/
|
|
644
|
-
export const renderStaticScene = (renderConfig, throttle) => {
|
|
645
|
-
if (throttle) {
|
|
646
|
-
renderStaticSceneThrottled(renderConfig);
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
_renderStaticScene(renderConfig);
|
|
650
|
-
};
|
|
651
|
-
export const cancelRender = () => {
|
|
652
|
-
renderInteractiveSceneThrottled.cancel();
|
|
653
|
-
renderStaticSceneThrottled.cancel();
|
|
654
|
-
};
|
|
655
|
-
const renderTransformHandles = (context, renderConfig, appState, transformHandles, angle) => {
|
|
656
|
-
Object.keys(transformHandles).forEach((key) => {
|
|
657
|
-
const transformHandle = transformHandles[key];
|
|
658
|
-
if (transformHandle !== undefined) {
|
|
659
|
-
const [x, y, width, height] = transformHandle;
|
|
660
|
-
context.save();
|
|
661
|
-
context.lineWidth = 1 / appState.zoom.value;
|
|
662
|
-
if (renderConfig.selectionColor) {
|
|
663
|
-
context.strokeStyle = renderConfig.selectionColor;
|
|
664
|
-
}
|
|
665
|
-
if (key === "rotation") {
|
|
666
|
-
fillCircle(context, x + width / 2, y + height / 2, width / 2);
|
|
667
|
-
// prefer round corners if roundRect API is available
|
|
668
|
-
}
|
|
669
|
-
else if (context.roundRect) {
|
|
670
|
-
context.beginPath();
|
|
671
|
-
context.roundRect(x, y, width, height, 2 / appState.zoom.value);
|
|
672
|
-
context.fill();
|
|
673
|
-
context.stroke();
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
strokeRectWithRotation(context, x, y, width, height, x + width / 2, y + height / 2, angle, true);
|
|
677
|
-
}
|
|
678
|
-
context.restore();
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
};
|
|
682
|
-
const renderSelectionBorder = (context, appState, elementProperties, padding = DEFAULT_SPACING * 2) => {
|
|
683
|
-
const { angle, elementX1, elementY1, elementX2, elementY2, selectionColors, cx, cy, dashed, activeEmbeddable, } = elementProperties;
|
|
684
|
-
const elementWidth = elementX2 - elementX1;
|
|
685
|
-
const elementHeight = elementY2 - elementY1;
|
|
686
|
-
const linePadding = padding / appState.zoom.value;
|
|
687
|
-
const lineWidth = 8 / appState.zoom.value;
|
|
688
|
-
const spaceWidth = 4 / appState.zoom.value;
|
|
689
|
-
context.save();
|
|
690
|
-
context.translate(appState.scrollX, appState.scrollY);
|
|
691
|
-
context.lineWidth = (activeEmbeddable ? 4 : 1) / appState.zoom.value;
|
|
692
|
-
const count = selectionColors.length;
|
|
693
|
-
for (let index = 0; index < count; ++index) {
|
|
694
|
-
context.strokeStyle = selectionColors[index];
|
|
695
|
-
if (dashed) {
|
|
696
|
-
context.setLineDash([
|
|
697
|
-
lineWidth,
|
|
698
|
-
spaceWidth + (lineWidth + spaceWidth) * (count - 1),
|
|
699
|
-
]);
|
|
700
|
-
}
|
|
701
|
-
context.lineDashOffset = (lineWidth + spaceWidth) * index;
|
|
702
|
-
strokeRectWithRotation(context, elementX1 - linePadding, elementY1 - linePadding, elementWidth + linePadding * 2, elementHeight + linePadding * 2, cx, cy, angle);
|
|
703
|
-
}
|
|
704
|
-
context.restore();
|
|
705
|
-
};
|
|
706
|
-
const renderBindingHighlight = (context, appState, suggestedBinding) => {
|
|
707
|
-
const renderHighlight = Array.isArray(suggestedBinding)
|
|
708
|
-
? renderBindingHighlightForSuggestedPointBinding
|
|
709
|
-
: renderBindingHighlightForBindableElement;
|
|
710
|
-
context.save();
|
|
711
|
-
context.translate(appState.scrollX, appState.scrollY);
|
|
712
|
-
renderHighlight(context, suggestedBinding);
|
|
713
|
-
context.restore();
|
|
714
|
-
};
|
|
715
|
-
const renderBindingHighlightForBindableElement = (context, element) => {
|
|
716
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
717
|
-
const width = x2 - x1;
|
|
718
|
-
const height = y2 - y1;
|
|
719
|
-
const threshold = maxBindingGap(element, width, height);
|
|
720
|
-
// So that we don't overlap the element itself
|
|
721
|
-
const strokeOffset = 4;
|
|
722
|
-
context.strokeStyle = "rgba(0,0,0,.05)";
|
|
723
|
-
context.lineWidth = threshold - strokeOffset;
|
|
724
|
-
const padding = strokeOffset / 2 + threshold / 2;
|
|
725
|
-
switch (element.type) {
|
|
726
|
-
case "rectangle":
|
|
727
|
-
case "text":
|
|
728
|
-
case "image":
|
|
729
|
-
case "iframe":
|
|
730
|
-
case "embeddable":
|
|
731
|
-
case "frame":
|
|
732
|
-
case "magicframe":
|
|
733
|
-
strokeRectWithRotation(context, x1 - padding, y1 - padding, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
734
|
-
break;
|
|
735
|
-
case "diamond":
|
|
736
|
-
const side = Math.hypot(width, height);
|
|
737
|
-
const wPadding = (padding * side) / height;
|
|
738
|
-
const hPadding = (padding * side) / width;
|
|
739
|
-
strokeDiamondWithRotation(context, width + wPadding * 2, height + hPadding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
740
|
-
break;
|
|
741
|
-
case "ellipse":
|
|
742
|
-
strokeEllipseWithRotation(context, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle);
|
|
743
|
-
break;
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
const renderFrameHighlight = (context, appState, frame) => {
|
|
747
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(frame);
|
|
748
|
-
const width = x2 - x1;
|
|
749
|
-
const height = y2 - y1;
|
|
750
|
-
context.strokeStyle = "rgb(0,118,255)";
|
|
751
|
-
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
|
752
|
-
context.save();
|
|
753
|
-
context.translate(appState.scrollX, appState.scrollY);
|
|
754
|
-
strokeRectWithRotation(context, x1, y1, width, height, x1 + width / 2, y1 + height / 2, frame.angle, false, FRAME_STYLE.radius / appState.zoom.value);
|
|
755
|
-
context.restore();
|
|
756
|
-
};
|
|
757
|
-
const renderElementsBoxHighlight = (context, appState, elements) => {
|
|
758
|
-
const individualElements = elements.filter((element) => element.groupIds.length === 0);
|
|
759
|
-
const elementsInGroups = elements.filter((element) => element.groupIds.length > 0);
|
|
760
|
-
const getSelectionFromElements = (elements) => {
|
|
761
|
-
const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(elements);
|
|
762
|
-
return {
|
|
763
|
-
angle: 0,
|
|
764
|
-
elementX1,
|
|
765
|
-
elementX2,
|
|
766
|
-
elementY1,
|
|
767
|
-
elementY2,
|
|
768
|
-
selectionColors: ["rgb(0,118,255)"],
|
|
769
|
-
dashed: false,
|
|
770
|
-
cx: elementX1 + (elementX2 - elementX1) / 2,
|
|
771
|
-
cy: elementY1 + (elementY2 - elementY1) / 2,
|
|
772
|
-
activeEmbeddable: false,
|
|
773
|
-
};
|
|
774
|
-
};
|
|
775
|
-
const getSelectionForGroupId = (groupId) => {
|
|
776
|
-
const groupElements = getElementsInGroup(elements, groupId);
|
|
777
|
-
return getSelectionFromElements(groupElements);
|
|
778
|
-
};
|
|
779
|
-
Object.entries(selectGroupsFromGivenElements(elementsInGroups, appState))
|
|
780
|
-
.filter(([id, isSelected]) => isSelected)
|
|
781
|
-
.map(([id, isSelected]) => id)
|
|
782
|
-
.map((groupId) => getSelectionForGroupId(groupId))
|
|
783
|
-
.concat(individualElements.map((element) => getSelectionFromElements([element])))
|
|
784
|
-
.forEach((selection) => renderSelectionBorder(context, appState, selection));
|
|
785
|
-
};
|
|
786
|
-
const renderBindingHighlightForSuggestedPointBinding = (context, suggestedBinding) => {
|
|
787
|
-
const [element, startOrEnd, bindableElement] = suggestedBinding;
|
|
788
|
-
const threshold = maxBindingGap(bindableElement, bindableElement.width, bindableElement.height);
|
|
789
|
-
context.strokeStyle = "rgba(0,0,0,0)";
|
|
790
|
-
context.fillStyle = "rgba(0,0,0,.05)";
|
|
791
|
-
const pointIndices = startOrEnd === "both" ? [0, -1] : startOrEnd === "start" ? [0] : [-1];
|
|
792
|
-
pointIndices.forEach((index) => {
|
|
793
|
-
const [x, y] = LinearElementEditor.getPointAtIndexGlobalCoordinates(element, index);
|
|
794
|
-
fillCircle(context, x, y, threshold);
|
|
795
|
-
});
|
|
796
|
-
};
|
|
797
|
-
let linkCanvasCache;
|
|
798
|
-
const renderLinkIcon = (element, context, appState) => {
|
|
799
|
-
if (element.link && !appState.selectedElementIds[element.id]) {
|
|
800
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
801
|
-
const [x, y, width, height] = getLinkHandleFromCoords([x1, y1, x2, y2], element.angle, appState);
|
|
802
|
-
const centerX = x + width / 2;
|
|
803
|
-
const centerY = y + height / 2;
|
|
804
|
-
context.save();
|
|
805
|
-
context.translate(appState.scrollX + centerX, appState.scrollY + centerY);
|
|
806
|
-
context.rotate(element.angle);
|
|
807
|
-
if (!linkCanvasCache || linkCanvasCache.zoom !== appState.zoom.value) {
|
|
808
|
-
linkCanvasCache = document.createElement("canvas");
|
|
809
|
-
linkCanvasCache.zoom = appState.zoom.value;
|
|
810
|
-
linkCanvasCache.width =
|
|
811
|
-
width * window.devicePixelRatio * appState.zoom.value;
|
|
812
|
-
linkCanvasCache.height =
|
|
813
|
-
height * window.devicePixelRatio * appState.zoom.value;
|
|
814
|
-
const linkCanvasCacheContext = linkCanvasCache.getContext("2d");
|
|
815
|
-
linkCanvasCacheContext.scale(window.devicePixelRatio * appState.zoom.value, window.devicePixelRatio * appState.zoom.value);
|
|
816
|
-
linkCanvasCacheContext.fillStyle = "#fff";
|
|
817
|
-
linkCanvasCacheContext.fillRect(0, 0, width, height);
|
|
818
|
-
linkCanvasCacheContext.drawImage(EXTERNAL_LINK_IMG, 0, 0, width, height);
|
|
819
|
-
linkCanvasCacheContext.restore();
|
|
820
|
-
context.drawImage(linkCanvasCache, x - centerX, y - centerY, width, height);
|
|
821
|
-
}
|
|
822
|
-
else {
|
|
823
|
-
context.drawImage(linkCanvasCache, x - centerX, y - centerY, width, height);
|
|
824
|
-
}
|
|
825
|
-
context.restore();
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
|
-
// This should be only called for exporting purposes
|
|
829
|
-
export const renderSceneToSvg = (elements, rsvg, svgRoot, files, renderConfig) => {
|
|
830
|
-
if (!svgRoot) {
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
833
|
-
// render elements
|
|
834
|
-
elements
|
|
835
|
-
.filter((el) => !isIframeLikeOrItsLabel(el))
|
|
836
|
-
.forEach((element) => {
|
|
837
|
-
if (!element.isDeleted) {
|
|
838
|
-
try {
|
|
839
|
-
renderElementToSvg(element, rsvg, svgRoot, files, element.x + renderConfig.offsetX, element.y + renderConfig.offsetY, renderConfig);
|
|
840
|
-
}
|
|
841
|
-
catch (error) {
|
|
842
|
-
console.error(error);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
});
|
|
846
|
-
// render embeddables on top
|
|
847
|
-
elements
|
|
848
|
-
.filter((el) => isIframeLikeElement(el))
|
|
849
|
-
.forEach((element) => {
|
|
850
|
-
if (!element.isDeleted) {
|
|
851
|
-
try {
|
|
852
|
-
renderElementToSvg(element, rsvg, svgRoot, files, element.x + renderConfig.offsetX, element.y + renderConfig.offsetY, renderConfig);
|
|
853
|
-
}
|
|
854
|
-
catch (error) {
|
|
855
|
-
console.error(error);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
|
-
};
|