@excalidraw/excalidraw 0.17.1-7500-ac247a0 → 0.17.1-b7babe5
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 +56 -2
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-2W5GQUR4.js → chunk-6NMK7JTV.js} +13 -6
- package/dist/browser/dev/excalidraw-assets-dev/chunk-6NMK7JTV.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/chunk-CX3RATXT.js +20324 -0
- package/dist/browser/dev/excalidraw-assets-dev/chunk-CX3RATXT.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-OC6JWP3X.js → en-BZY7JRTM.js} +4 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-5TVMINCA.js → image-CVN3YKRW.js} +2 -4
- 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/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 +34964 -37
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/chunk-VJAIK3AX.js +55 -0
- package/dist/browser/prod/excalidraw-assets/chunk-YYO5DFUW.js +11 -0
- package/dist/browser/prod/excalidraw-assets/en-O2YCQM2W.js +1 -0
- package/dist/browser/prod/excalidraw-assets/image-6FKY54X5.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-EY7E2L5O.json} +10 -5
- package/dist/dev/index.css +189 -129
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +38702 -39409
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionAddToLibrary.d.ts +15 -15
- package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
- package/dist/excalidraw/actions/actionAlign.js +2 -1
- package/dist/excalidraw/actions/actionBoundText.d.ts +10 -10
- package/dist/excalidraw/actions/actionBoundText.js +8 -8
- package/dist/excalidraw/actions/actionCanvas.d.ts +58 -58
- package/dist/excalidraw/actions/actionClipboard.d.ts +34 -34
- package/dist/excalidraw/actions/actionClipboard.js +9 -2
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +15 -15
- 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 +10 -10
- package/dist/excalidraw/actions/actionExport.d.ts +43 -43
- package/dist/excalidraw/actions/actionExport.js +4 -4
- package/dist/excalidraw/actions/actionFinalize.d.ts +9 -9
- 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 +16 -16
- package/dist/excalidraw/actions/actionFrame.js +1 -1
- package/dist/excalidraw/actions/actionGroup.d.ts +10 -10
- package/dist/excalidraw/actions/actionGroup.js +3 -2
- package/dist/excalidraw/actions/actionLinearEditor.d.ts +5 -5
- package/dist/excalidraw/actions/actionLinearEditor.js +1 -1
- package/dist/excalidraw/{element/Hyperlink.d.ts → actions/actionLink.d.ts} +29 -51
- package/dist/excalidraw/actions/actionLink.js +40 -0
- package/dist/excalidraw/actions/actionMenu.d.ts +13 -13
- package/dist/excalidraw/actions/actionNavigate.d.ts +10 -10
- package/dist/excalidraw/actions/actionNavigate.js +1 -1
- package/dist/excalidraw/actions/actionProperties.d.ts +77 -77
- package/dist/excalidraw/actions/actionProperties.js +32 -27
- package/dist/excalidraw/actions/actionSelectAll.d.ts +5 -5
- package/dist/excalidraw/actions/actionSelectAll.js +1 -1
- package/dist/excalidraw/actions/actionStyles.d.ts +7 -7
- package/dist/excalidraw/actions/actionStyles.js +4 -4
- package/dist/excalidraw/actions/actionToggleGridMode.d.ts +5 -5
- package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +5 -5
- package/dist/excalidraw/actions/actionToggleStats.d.ts +5 -5
- package/dist/excalidraw/actions/actionToggleViewMode.d.ts +5 -5
- package/dist/excalidraw/actions/actionToggleZenMode.d.ts +5 -5
- 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 +23 -16
- package/dist/excalidraw/components/App.js +387 -272
- 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 +17 -13
- 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/PublishLibrary.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/Sidebar/Sidebar.d.ts +1 -1
- 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/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 +6 -0
- package/dist/excalidraw/constants.js +6 -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/library.d.ts +60 -8
- package/dist/excalidraw/data/library.js +302 -33
- 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 +12 -13
- package/dist/excalidraw/element/embeddable.js +17 -27
- package/dist/excalidraw/element/image.js +1 -2
- package/dist/excalidraw/element/index.d.ts +8 -1
- package/dist/excalidraw/element/index.js +23 -1
- package/dist/excalidraw/element/linearElementEditor.d.ts +36 -36
- 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 +34 -21
- package/dist/excalidraw/element/textElement.js +87 -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/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/hooks/useLibraryItemSvg.js +1 -1
- package/dist/excalidraw/index.d.ts +9 -10
- package/dist/excalidraw/index.js +16 -12
- package/dist/excalidraw/laser-trails.d.ts +19 -0
- package/dist/excalidraw/laser-trails.js +95 -0
- package/dist/excalidraw/locales/en.json +10 -5
- package/dist/excalidraw/queue.d.ts +9 -0
- package/dist/excalidraw/queue.js +27 -0
- 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 +16 -17
- package/dist/excalidraw/utility-types.d.ts +7 -0
- package/dist/excalidraw/utils.d.ts +21 -16
- package/dist/excalidraw/utils.js +43 -45
- package/dist/{dev/en-RLIAOBCI.json → prod/en-EY7E2L5O.json} +10 -5
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +42 -42
- package/dist/utils/bbox.d.ts +2 -2
- package/dist/utils/export.d.ts +3 -3
- package/dist/utils/export.js +3 -13
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/utils/withinBounds.d.ts +1 -1
- package/dist/utils/withinBounds.js +5 -2
- 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-KGZXLFLR.js +0 -53497
- package/dist/browser/dev/excalidraw-assets-dev/chunk-KGZXLFLR.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css +0 -5797
- package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-4YN2HN3S.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-LTLHTTSE.js +0 -1
- package/dist/browser/prod/excalidraw-assets/image-QBL334OA.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-BZY7JRTM.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-5TVMINCA.js.map → image-CVN3YKRW.js.map} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ExcalidrawElement, ExcalidrawTextElement, NonDeletedExcalidrawElement, ExcalidrawFreeDrawElement } from "../element/types";
|
|
1
|
+
import { ExcalidrawElement, ExcalidrawTextElement, NonDeletedExcalidrawElement, ExcalidrawFreeDrawElement, ExcalidrawFrameLikeElement, NonDeletedSceneElementsMap } from "../element/types";
|
|
2
2
|
import type { RoughCanvas } from "roughjs/bin/canvas";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { StaticCanvasRenderConfig, RenderableElementsMap } from "../scene/types";
|
|
4
|
+
import { AppState, StaticCanvasAppState, InteractiveCanvasAppState, ElementsPendingErasure } from "../types";
|
|
5
|
+
export declare const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
|
|
6
|
+
export declare const getRenderOpacity: (element: ExcalidrawElement, containingFrame: ExcalidrawFrameLikeElement | null, elementsPendingErasure: ElementsPendingErasure) => number;
|
|
6
7
|
export interface ExcalidrawElementWithCanvas {
|
|
7
8
|
element: ExcalidrawElement | ExcalidrawTextElement;
|
|
8
9
|
canvas: HTMLCanvasElement;
|
|
@@ -17,8 +18,7 @@ export interface ExcalidrawElementWithCanvas {
|
|
|
17
18
|
export declare const DEFAULT_LINK_SIZE = 14;
|
|
18
19
|
export declare const elementWithCanvasCache: WeakMap<ExcalidrawElement, ExcalidrawElementWithCanvas>;
|
|
19
20
|
export declare const renderSelectionElement: (element: NonDeletedExcalidrawElement, context: CanvasRenderingContext2D, appState: InteractiveCanvasAppState) => void;
|
|
20
|
-
export declare const renderElement: (element: NonDeletedExcalidrawElement, rc: RoughCanvas, context: CanvasRenderingContext2D, renderConfig: StaticCanvasRenderConfig, appState: StaticCanvasAppState) => void;
|
|
21
|
-
export declare const renderElementToSvg: (element: NonDeletedExcalidrawElement, rsvg: RoughSVG, svgRoot: SVGElement, files: BinaryFiles, offsetX: number, offsetY: number, renderConfig: SVGRenderConfig) => void;
|
|
21
|
+
export declare const renderElement: (element: NonDeletedExcalidrawElement, elementsMap: RenderableElementsMap, allElementsMap: NonDeletedSceneElementsMap, rc: RoughCanvas, context: CanvasRenderingContext2D, renderConfig: StaticCanvasRenderConfig, appState: StaticCanvasAppState) => void;
|
|
22
22
|
export declare const pathsCache: WeakMap<ExcalidrawFreeDrawElement, Path2D>;
|
|
23
23
|
export declare function generateFreeDrawShape(element: ExcalidrawFreeDrawElement): Path2D;
|
|
24
24
|
export declare function getFreeDrawPath2D(element: ExcalidrawFreeDrawElement): Path2D | undefined;
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { isTextElement, isLinearElement, isFreeDrawElement, isInitializedImageElement, isArrowElement, hasBoundTextElement, isMagicFrameElement, } from "../element/typeChecks";
|
|
2
2
|
import { getElementAbsoluteCoords } from "../element/bounds";
|
|
3
|
-
import { distance, getFontString,
|
|
4
|
-
import { getCornerRadius,
|
|
3
|
+
import { distance, getFontString, isRTL } from "../utils";
|
|
4
|
+
import { getCornerRadius, isRightAngle } from "../math";
|
|
5
5
|
import rough from "roughjs/bin/rough";
|
|
6
6
|
import { getDefaultAppState } from "../appState";
|
|
7
|
-
import { BOUND_TEXT_PADDING,
|
|
7
|
+
import { BOUND_TEXT_PADDING, ELEMENT_READY_TO_ERASE_OPACITY, FRAME_STYLE, MIME_TYPES, } from "../constants";
|
|
8
8
|
import { getStroke } from "perfect-freehand";
|
|
9
|
-
import { getBoundTextElement, getContainerCoords, getContainerElement, getLineHeightInPx, getBoundTextMaxHeight, getBoundTextMaxWidth, } from "../element/textElement";
|
|
9
|
+
import { getBoundTextElement, getContainerCoords, getContainerElement, getLineHeightInPx, getBoundTextMaxHeight, getBoundTextMaxWidth, getVerticalOffset, } from "../element/textElement";
|
|
10
10
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
11
|
-
import { createPlaceholderEmbeddableLabel, getEmbedLink, } from "../element/embeddable";
|
|
12
11
|
import { getContainingFrame } from "../frame";
|
|
13
|
-
import { normalizeLink, toValidURL } from "../data/url";
|
|
14
12
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
15
13
|
// using a stronger invert (100% vs our regular 93%) and saturate
|
|
16
14
|
// as a temp hack to make images in dark theme look closer to original
|
|
17
15
|
// color scheme (it's still not quite there and the colors look slightly
|
|
18
16
|
// desatured, alas...)
|
|
19
|
-
const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
|
|
17
|
+
export const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
|
|
20
18
|
const defaultAppState = getDefaultAppState();
|
|
21
19
|
const isPendingImageElement = (element, renderConfig) => isInitializedImageElement(element) &&
|
|
22
20
|
!renderConfig.imageCache.has(element.fileId);
|
|
@@ -27,7 +25,19 @@ const shouldResetImageFilter = (element, renderConfig, appState) => {
|
|
|
27
25
|
renderConfig.imageCache.get(element.fileId)?.mimeType !== MIME_TYPES.svg);
|
|
28
26
|
};
|
|
29
27
|
const getCanvasPadding = (element) => element.type === "freedraw" ? element.strokeWidth * 12 : 20;
|
|
30
|
-
const
|
|
28
|
+
export const getRenderOpacity = (element, containingFrame, elementsPendingErasure) => {
|
|
29
|
+
// multiplying frame opacity with element opacity to combine them
|
|
30
|
+
// (e.g. frame 50% and element 50% opacity should result in 25% opacity)
|
|
31
|
+
let opacity = ((containingFrame?.opacity ?? 100) * element.opacity) / 10000;
|
|
32
|
+
// if pending erasure, multiply again to combine further
|
|
33
|
+
// (so that erasing always results in lower opacity than original)
|
|
34
|
+
if (elementsPendingErasure.has(element.id) ||
|
|
35
|
+
(containingFrame && elementsPendingErasure.has(containingFrame.id))) {
|
|
36
|
+
opacity *= ELEMENT_READY_TO_ERASE_OPACITY / 100;
|
|
37
|
+
}
|
|
38
|
+
return opacity;
|
|
39
|
+
};
|
|
40
|
+
const cappedElementCanvasSize = (element, elementsMap, zoom) => {
|
|
31
41
|
// these limits are ballpark, they depend on specific browsers and device.
|
|
32
42
|
// We've chosen lower limits to be safe. We might want to change these limits
|
|
33
43
|
// based on browser/device type, if we get reports of low quality rendering
|
|
@@ -38,7 +48,7 @@ const cappedElementCanvasSize = (element, zoom) => {
|
|
|
38
48
|
// ~ safari width/height limit based on developer.mozilla.org.
|
|
39
49
|
const WIDTH_HEIGHT_LIMIT = 32767;
|
|
40
50
|
const padding = getCanvasPadding(element);
|
|
41
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
51
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
42
52
|
const elementWidth = isLinearElement(element) || isFreeDrawElement(element)
|
|
43
53
|
? distance(x1, x2)
|
|
44
54
|
: element.width;
|
|
@@ -61,17 +71,17 @@ const cappedElementCanvasSize = (element, zoom) => {
|
|
|
61
71
|
height = Math.floor(height * scale);
|
|
62
72
|
return { width, height, scale };
|
|
63
73
|
};
|
|
64
|
-
const generateElementCanvas = (element, zoom, renderConfig, appState) => {
|
|
74
|
+
const generateElementCanvas = (element, elementsMap, zoom, renderConfig, appState) => {
|
|
65
75
|
const canvas = document.createElement("canvas");
|
|
66
76
|
const context = canvas.getContext("2d");
|
|
67
77
|
const padding = getCanvasPadding(element);
|
|
68
|
-
const { width, height, scale } = cappedElementCanvasSize(element, zoom);
|
|
78
|
+
const { width, height, scale } = cappedElementCanvasSize(element, elementsMap, zoom);
|
|
69
79
|
canvas.width = width;
|
|
70
80
|
canvas.height = height;
|
|
71
81
|
let canvasOffsetX = 0;
|
|
72
82
|
let canvasOffsetY = 0;
|
|
73
83
|
if (isLinearElement(element) || isFreeDrawElement(element)) {
|
|
74
|
-
const [x1, y1] = getElementAbsoluteCoords(element);
|
|
84
|
+
const [x1, y1] = getElementAbsoluteCoords(element, elementsMap);
|
|
75
85
|
canvasOffsetX =
|
|
76
86
|
element.x > x1
|
|
77
87
|
? distance(element.x, x1) * window.devicePixelRatio * scale
|
|
@@ -100,8 +110,8 @@ const generateElementCanvas = (element, zoom, renderConfig, appState) => {
|
|
|
100
110
|
zoomValue: zoom.value,
|
|
101
111
|
canvasOffsetX,
|
|
102
112
|
canvasOffsetY,
|
|
103
|
-
boundTextElementVersion: getBoundTextElement(element)?.version || null,
|
|
104
|
-
containingFrameOpacity: getContainingFrame(element)?.opacity || 100,
|
|
113
|
+
boundTextElementVersion: getBoundTextElement(element, elementsMap)?.version || null,
|
|
114
|
+
containingFrameOpacity: getContainingFrame(element, elementsMap)?.opacity || 100,
|
|
105
115
|
};
|
|
106
116
|
};
|
|
107
117
|
export const DEFAULT_LINK_SIZE = 14;
|
|
@@ -119,8 +129,6 @@ const drawImagePlaceholder = (element, context, zoomValue) => {
|
|
|
119
129
|
: IMAGE_PLACEHOLDER_IMG, element.width / 2 - size / 2, element.height / 2 - size / 2, size, size);
|
|
120
130
|
};
|
|
121
131
|
const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
|
|
122
|
-
context.globalAlpha =
|
|
123
|
-
((getContainingFrame(element)?.opacity ?? 100) * element.opacity) / 10000;
|
|
124
132
|
switch (element.type) {
|
|
125
133
|
case "rectangle":
|
|
126
134
|
case "iframe":
|
|
@@ -160,6 +168,11 @@ const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
|
|
|
160
168
|
? renderConfig.imageCache.get(element.fileId)?.image
|
|
161
169
|
: undefined;
|
|
162
170
|
if (img != null && !(img instanceof Promise)) {
|
|
171
|
+
if (element.roundness && context.roundRect) {
|
|
172
|
+
context.beginPath();
|
|
173
|
+
context.roundRect(0, 0, element.width, element.height, getCornerRadius(Math.min(element.width, element.height), element));
|
|
174
|
+
context.clip();
|
|
175
|
+
}
|
|
163
176
|
context.drawImage(img, 0 /* hardcoded for the selection box*/, 0, element.width, element.height);
|
|
164
177
|
}
|
|
165
178
|
else {
|
|
@@ -189,9 +202,9 @@ const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
|
|
|
189
202
|
? element.width
|
|
190
203
|
: 0;
|
|
191
204
|
const lineHeightPx = getLineHeightInPx(element.fontSize, element.lineHeight);
|
|
192
|
-
const verticalOffset = element.
|
|
205
|
+
const verticalOffset = getVerticalOffset(element.fontFamily, element.fontSize, lineHeightPx);
|
|
193
206
|
for (let index = 0; index < lines.length; index++) {
|
|
194
|
-
context.fillText(lines[index], horizontalOffset,
|
|
207
|
+
context.fillText(lines[index], horizontalOffset, index * lineHeightPx + verticalOffset);
|
|
195
208
|
}
|
|
196
209
|
context.restore();
|
|
197
210
|
if (shouldTemporarilyAttach) {
|
|
@@ -203,33 +216,32 @@ const drawElementOnCanvas = (element, rc, context, renderConfig, appState) => {
|
|
|
203
216
|
}
|
|
204
217
|
}
|
|
205
218
|
}
|
|
206
|
-
context.globalAlpha = 1;
|
|
207
219
|
};
|
|
208
220
|
export const elementWithCanvasCache = new WeakMap();
|
|
209
|
-
const generateElementWithCanvas = (element, renderConfig, appState) => {
|
|
221
|
+
const generateElementWithCanvas = (element, elementsMap, renderConfig, appState) => {
|
|
210
222
|
const zoom = renderConfig ? appState.zoom : defaultAppState.zoom;
|
|
211
223
|
const prevElementWithCanvas = elementWithCanvasCache.get(element);
|
|
212
224
|
const shouldRegenerateBecauseZoom = prevElementWithCanvas &&
|
|
213
225
|
prevElementWithCanvas.zoomValue !== zoom.value &&
|
|
214
226
|
!appState?.shouldCacheIgnoreZoom;
|
|
215
|
-
const boundTextElementVersion = getBoundTextElement(element)?.version || null;
|
|
216
|
-
const containingFrameOpacity = getContainingFrame(element)?.opacity || 100;
|
|
227
|
+
const boundTextElementVersion = getBoundTextElement(element, elementsMap)?.version || null;
|
|
228
|
+
const containingFrameOpacity = getContainingFrame(element, elementsMap)?.opacity || 100;
|
|
217
229
|
if (!prevElementWithCanvas ||
|
|
218
230
|
shouldRegenerateBecauseZoom ||
|
|
219
231
|
prevElementWithCanvas.theme !== appState.theme ||
|
|
220
232
|
prevElementWithCanvas.boundTextElementVersion !== boundTextElementVersion ||
|
|
221
233
|
prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity) {
|
|
222
|
-
const elementWithCanvas = generateElementCanvas(element, zoom, renderConfig, appState);
|
|
234
|
+
const elementWithCanvas = generateElementCanvas(element, elementsMap, zoom, renderConfig, appState);
|
|
223
235
|
elementWithCanvasCache.set(element, elementWithCanvas);
|
|
224
236
|
return elementWithCanvas;
|
|
225
237
|
}
|
|
226
238
|
return prevElementWithCanvas;
|
|
227
239
|
};
|
|
228
|
-
const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState) => {
|
|
240
|
+
const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState, allElementsMap) => {
|
|
229
241
|
const element = elementWithCanvas.element;
|
|
230
242
|
const padding = getCanvasPadding(element);
|
|
231
243
|
const zoom = elementWithCanvas.scale;
|
|
232
|
-
let [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
244
|
+
let [x1, y1, x2, y2] = getElementAbsoluteCoords(element, allElementsMap);
|
|
233
245
|
// Free draw elements will otherwise "shuffle" as the min x and y change
|
|
234
246
|
if (isFreeDrawElement(element)) {
|
|
235
247
|
x1 = Math.floor(x1);
|
|
@@ -241,7 +253,7 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
|
|
|
241
253
|
const cy = ((y1 + y2) / 2 + appState.scrollY) * window.devicePixelRatio;
|
|
242
254
|
context.save();
|
|
243
255
|
context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
|
|
244
|
-
const boundTextElement = getBoundTextElement(element);
|
|
256
|
+
const boundTextElement = getBoundTextElement(element, allElementsMap);
|
|
245
257
|
if (isArrowElement(element) && boundTextElement) {
|
|
246
258
|
const tempCanvas = document.createElement("canvas");
|
|
247
259
|
const tempCanvasContext = tempCanvas.getContext("2d");
|
|
@@ -259,7 +271,7 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
|
|
|
259
271
|
tempCanvasContext.translate(tempCanvas.width / 2, tempCanvas.height / 2);
|
|
260
272
|
tempCanvasContext.rotate(element.angle);
|
|
261
273
|
tempCanvasContext.drawImage(elementWithCanvas.canvas, -elementWithCanvas.canvas.width / 2, -elementWithCanvas.canvas.height / 2, elementWithCanvas.canvas.width, elementWithCanvas.canvas.height);
|
|
262
|
-
const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement);
|
|
274
|
+
const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement, allElementsMap);
|
|
263
275
|
tempCanvasContext.rotate(-element.angle);
|
|
264
276
|
// Shift the canvas to the center of the bound text element
|
|
265
277
|
const shiftX = tempCanvas.width / 2 -
|
|
@@ -301,11 +313,11 @@ const drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appStat
|
|
|
301
313
|
if (import.meta.env.VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX ===
|
|
302
314
|
"true" &&
|
|
303
315
|
hasBoundTextElement(element)) {
|
|
304
|
-
const textElement = getBoundTextElement(element);
|
|
316
|
+
const textElement = getBoundTextElement(element, allElementsMap);
|
|
305
317
|
const coords = getContainerCoords(element);
|
|
306
318
|
context.strokeStyle = "#c92a2a";
|
|
307
319
|
context.lineWidth = 3;
|
|
308
|
-
context.strokeRect((coords.x + appState.scrollX) * window.devicePixelRatio, (coords.y + appState.scrollY) * window.devicePixelRatio, getBoundTextMaxWidth(element) * window.devicePixelRatio, getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio);
|
|
320
|
+
context.strokeRect((coords.x + appState.scrollX) * window.devicePixelRatio, (coords.y + appState.scrollY) * window.devicePixelRatio, getBoundTextMaxWidth(element, textElement) * window.devicePixelRatio, getBoundTextMaxHeight(element, textElement) * window.devicePixelRatio);
|
|
309
321
|
}
|
|
310
322
|
}
|
|
311
323
|
context.restore();
|
|
@@ -326,7 +338,8 @@ export const renderSelectionElement = (element, context, appState) => {
|
|
|
326
338
|
context.strokeRect(offset, offset, element.width, element.height);
|
|
327
339
|
context.restore();
|
|
328
340
|
};
|
|
329
|
-
export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
341
|
+
export const renderElement = (element, elementsMap, allElementsMap, rc, context, renderConfig, appState) => {
|
|
342
|
+
context.globalAlpha = getRenderOpacity(element, getContainingFrame(element, elementsMap), renderConfig.elementsPendingErasure);
|
|
330
343
|
switch (element.type) {
|
|
331
344
|
case "magicframe":
|
|
332
345
|
case "frame": {
|
|
@@ -360,7 +373,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
360
373
|
// rely on existing shapes
|
|
361
374
|
ShapeCache.generateElementShape(element, null);
|
|
362
375
|
if (renderConfig.isExporting) {
|
|
363
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
376
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
364
377
|
const cx = (x1 + x2) / 2 + appState.scrollX;
|
|
365
378
|
const cy = (y1 + y2) / 2 + appState.scrollY;
|
|
366
379
|
const shiftX = (x2 - x1) / 2 - (element.x - x1);
|
|
@@ -373,8 +386,8 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
373
386
|
context.restore();
|
|
374
387
|
}
|
|
375
388
|
else {
|
|
376
|
-
const elementWithCanvas = generateElementWithCanvas(element, renderConfig, appState);
|
|
377
|
-
drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState);
|
|
389
|
+
const elementWithCanvas = generateElementWithCanvas(element, elementsMap, renderConfig, appState);
|
|
390
|
+
drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState, allElementsMap);
|
|
378
391
|
}
|
|
379
392
|
break;
|
|
380
393
|
}
|
|
@@ -392,15 +405,15 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
392
405
|
// rely on existing shapes
|
|
393
406
|
ShapeCache.generateElementShape(element, renderConfig);
|
|
394
407
|
if (renderConfig.isExporting) {
|
|
395
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
408
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
396
409
|
const cx = (x1 + x2) / 2 + appState.scrollX;
|
|
397
410
|
const cy = (y1 + y2) / 2 + appState.scrollY;
|
|
398
411
|
let shiftX = (x2 - x1) / 2 - (element.x - x1);
|
|
399
412
|
let shiftY = (y2 - y1) / 2 - (element.y - y1);
|
|
400
413
|
if (isTextElement(element)) {
|
|
401
|
-
const container = getContainerElement(element);
|
|
414
|
+
const container = getContainerElement(element, elementsMap);
|
|
402
415
|
if (isArrowElement(container)) {
|
|
403
|
-
const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, element);
|
|
416
|
+
const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, element, elementsMap);
|
|
404
417
|
shiftX = (x2 - x1) / 2 - (boundTextCoords.x - x1);
|
|
405
418
|
shiftY = (y2 - y1) / 2 - (boundTextCoords.y - y1);
|
|
406
419
|
}
|
|
@@ -410,7 +423,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
410
423
|
if (shouldResetImageFilter(element, renderConfig, appState)) {
|
|
411
424
|
context.filter = "none";
|
|
412
425
|
}
|
|
413
|
-
const boundTextElement = getBoundTextElement(element);
|
|
426
|
+
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
414
427
|
if (isArrowElement(element) && boundTextElement) {
|
|
415
428
|
const tempCanvas = document.createElement("canvas");
|
|
416
429
|
const tempCanvasContext = tempCanvas.getContext("2d");
|
|
@@ -434,7 +447,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
434
447
|
tempCanvasContext.translate(shiftX, shiftY);
|
|
435
448
|
tempCanvasContext.rotate(-element.angle);
|
|
436
449
|
// Shift the canvas to center of bound text
|
|
437
|
-
const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement);
|
|
450
|
+
const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords(boundTextElement, elementsMap);
|
|
438
451
|
const boundTextShiftX = (x1 + x2) / 2 - boundTextCx;
|
|
439
452
|
const boundTextShiftY = (y1 + y2) / 2 - boundTextCy;
|
|
440
453
|
tempCanvasContext.translate(-boundTextShiftX, -boundTextShiftY);
|
|
@@ -457,7 +470,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
457
470
|
// canvases)
|
|
458
471
|
}
|
|
459
472
|
else {
|
|
460
|
-
const elementWithCanvas = generateElementWithCanvas(element, renderConfig, appState);
|
|
473
|
+
const elementWithCanvas = generateElementWithCanvas(element, elementsMap, renderConfig, appState);
|
|
461
474
|
const currentImageSmoothingStatus = context.imageSmoothingEnabled;
|
|
462
475
|
if (
|
|
463
476
|
// do not disable smoothing during zoom as blurry shapes look better
|
|
@@ -477,7 +490,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
477
490
|
//
|
|
478
491
|
context.imageSmoothingEnabled = false;
|
|
479
492
|
}
|
|
480
|
-
drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState);
|
|
493
|
+
drawElementFromCanvas(elementWithCanvas, context, renderConfig, appState, allElementsMap);
|
|
481
494
|
// reset
|
|
482
495
|
context.imageSmoothingEnabled = currentImageSmoothingStatus;
|
|
483
496
|
}
|
|
@@ -488,332 +501,7 @@ export const renderElement = (element, rc, context, renderConfig, appState) => {
|
|
|
488
501
|
throw new Error(`Unimplemented type ${element.type}`);
|
|
489
502
|
}
|
|
490
503
|
}
|
|
491
|
-
|
|
492
|
-
const roughSVGDrawWithPrecision = (rsvg, drawable, precision) => {
|
|
493
|
-
if (typeof precision === "undefined") {
|
|
494
|
-
return rsvg.draw(drawable);
|
|
495
|
-
}
|
|
496
|
-
const pshape = {
|
|
497
|
-
sets: drawable.sets,
|
|
498
|
-
shape: drawable.shape,
|
|
499
|
-
options: { ...drawable.options, fixedDecimalPlaceDigits: precision },
|
|
500
|
-
};
|
|
501
|
-
return rsvg.draw(pshape);
|
|
502
|
-
};
|
|
503
|
-
const maybeWrapNodesInFrameClipPath = (element, root, nodes, frameRendering) => {
|
|
504
|
-
if (!frameRendering.enabled || !frameRendering.clip) {
|
|
505
|
-
return null;
|
|
506
|
-
}
|
|
507
|
-
const frame = getContainingFrame(element);
|
|
508
|
-
if (frame) {
|
|
509
|
-
const g = root.ownerDocument.createElementNS(SVG_NS, "g");
|
|
510
|
-
g.setAttributeNS(SVG_NS, "clip-path", `url(#${frame.id})`);
|
|
511
|
-
nodes.forEach((node) => g.appendChild(node));
|
|
512
|
-
return g;
|
|
513
|
-
}
|
|
514
|
-
return null;
|
|
515
|
-
};
|
|
516
|
-
export const renderElementToSvg = (element, rsvg, svgRoot, files, offsetX, offsetY, renderConfig) => {
|
|
517
|
-
const offset = { x: offsetX, y: offsetY };
|
|
518
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
519
|
-
let cx = (x2 - x1) / 2 - (element.x - x1);
|
|
520
|
-
let cy = (y2 - y1) / 2 - (element.y - y1);
|
|
521
|
-
if (isTextElement(element)) {
|
|
522
|
-
const container = getContainerElement(element);
|
|
523
|
-
if (isArrowElement(container)) {
|
|
524
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(container);
|
|
525
|
-
const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(container, element);
|
|
526
|
-
cx = (x2 - x1) / 2 - (boundTextCoords.x - x1);
|
|
527
|
-
cy = (y2 - y1) / 2 - (boundTextCoords.y - y1);
|
|
528
|
-
offsetX = offsetX + boundTextCoords.x - element.x;
|
|
529
|
-
offsetY = offsetY + boundTextCoords.y - element.y;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const degree = (180 * element.angle) / Math.PI;
|
|
533
|
-
// element to append node to, most of the time svgRoot
|
|
534
|
-
let root = svgRoot;
|
|
535
|
-
// if the element has a link, create an anchor tag and make that the new root
|
|
536
|
-
if (element.link) {
|
|
537
|
-
const anchorTag = svgRoot.ownerDocument.createElementNS(SVG_NS, "a");
|
|
538
|
-
anchorTag.setAttribute("href", normalizeLink(element.link));
|
|
539
|
-
root.appendChild(anchorTag);
|
|
540
|
-
root = anchorTag;
|
|
541
|
-
}
|
|
542
|
-
const addToRoot = (node, element) => {
|
|
543
|
-
if (isTestEnv()) {
|
|
544
|
-
node.setAttribute("data-id", element.id);
|
|
545
|
-
}
|
|
546
|
-
root.appendChild(node);
|
|
547
|
-
};
|
|
548
|
-
const opacity = ((getContainingFrame(element)?.opacity ?? 100) * element.opacity) / 10000;
|
|
549
|
-
switch (element.type) {
|
|
550
|
-
case "selection": {
|
|
551
|
-
// Since this is used only during editing experience, which is canvas based,
|
|
552
|
-
// this should not happen
|
|
553
|
-
throw new Error("Selection rendering is not supported for SVG");
|
|
554
|
-
}
|
|
555
|
-
case "rectangle":
|
|
556
|
-
case "diamond":
|
|
557
|
-
case "ellipse": {
|
|
558
|
-
const shape = ShapeCache.generateElementShape(element, null);
|
|
559
|
-
const node = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
|
|
560
|
-
if (opacity !== 1) {
|
|
561
|
-
node.setAttribute("stroke-opacity", `${opacity}`);
|
|
562
|
-
node.setAttribute("fill-opacity", `${opacity}`);
|
|
563
|
-
}
|
|
564
|
-
node.setAttribute("stroke-linecap", "round");
|
|
565
|
-
node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
566
|
-
const g = maybeWrapNodesInFrameClipPath(element, root, [node], renderConfig.frameRendering);
|
|
567
|
-
addToRoot(g || node, element);
|
|
568
|
-
break;
|
|
569
|
-
}
|
|
570
|
-
case "iframe":
|
|
571
|
-
case "embeddable": {
|
|
572
|
-
// render placeholder rectangle
|
|
573
|
-
const shape = ShapeCache.generateElementShape(element, renderConfig);
|
|
574
|
-
const node = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
|
|
575
|
-
const opacity = element.opacity / 100;
|
|
576
|
-
if (opacity !== 1) {
|
|
577
|
-
node.setAttribute("stroke-opacity", `${opacity}`);
|
|
578
|
-
node.setAttribute("fill-opacity", `${opacity}`);
|
|
579
|
-
}
|
|
580
|
-
node.setAttribute("stroke-linecap", "round");
|
|
581
|
-
node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
582
|
-
addToRoot(node, element);
|
|
583
|
-
const label = createPlaceholderEmbeddableLabel(element);
|
|
584
|
-
renderElementToSvg(label, rsvg, root, files, label.x + offset.x - element.x, label.y + offset.y - element.y, renderConfig);
|
|
585
|
-
// render embeddable element + iframe
|
|
586
|
-
const embeddableNode = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
|
|
587
|
-
embeddableNode.setAttribute("stroke-linecap", "round");
|
|
588
|
-
embeddableNode.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
589
|
-
while (embeddableNode.firstChild) {
|
|
590
|
-
embeddableNode.removeChild(embeddableNode.firstChild);
|
|
591
|
-
}
|
|
592
|
-
const radius = getCornerRadius(Math.min(element.width, element.height), element);
|
|
593
|
-
const embedLink = getEmbedLink(toValidURL(element.link || ""));
|
|
594
|
-
// if rendering embeddables explicitly disabled or
|
|
595
|
-
// embedding documents via srcdoc (which doesn't seem to work for SVGs)
|
|
596
|
-
// replace with a link instead
|
|
597
|
-
if (renderConfig.renderEmbeddables === false ||
|
|
598
|
-
embedLink?.type === "document") {
|
|
599
|
-
const anchorTag = svgRoot.ownerDocument.createElementNS(SVG_NS, "a");
|
|
600
|
-
anchorTag.setAttribute("href", normalizeLink(element.link || ""));
|
|
601
|
-
anchorTag.setAttribute("target", "_blank");
|
|
602
|
-
anchorTag.setAttribute("rel", "noopener noreferrer");
|
|
603
|
-
anchorTag.style.borderRadius = `${radius}px`;
|
|
604
|
-
embeddableNode.appendChild(anchorTag);
|
|
605
|
-
}
|
|
606
|
-
else {
|
|
607
|
-
const foreignObject = svgRoot.ownerDocument.createElementNS(SVG_NS, "foreignObject");
|
|
608
|
-
foreignObject.style.width = `${element.width}px`;
|
|
609
|
-
foreignObject.style.height = `${element.height}px`;
|
|
610
|
-
foreignObject.style.border = "none";
|
|
611
|
-
const div = foreignObject.ownerDocument.createElementNS(SVG_NS, "div");
|
|
612
|
-
div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
613
|
-
div.style.width = "100%";
|
|
614
|
-
div.style.height = "100%";
|
|
615
|
-
const iframe = div.ownerDocument.createElement("iframe");
|
|
616
|
-
iframe.src = embedLink?.link ?? "";
|
|
617
|
-
iframe.style.width = "100%";
|
|
618
|
-
iframe.style.height = "100%";
|
|
619
|
-
iframe.style.border = "none";
|
|
620
|
-
iframe.style.borderRadius = `${radius}px`;
|
|
621
|
-
iframe.style.top = "0";
|
|
622
|
-
iframe.style.left = "0";
|
|
623
|
-
iframe.allowFullscreen = true;
|
|
624
|
-
div.appendChild(iframe);
|
|
625
|
-
foreignObject.appendChild(div);
|
|
626
|
-
embeddableNode.appendChild(foreignObject);
|
|
627
|
-
}
|
|
628
|
-
addToRoot(embeddableNode, element);
|
|
629
|
-
break;
|
|
630
|
-
}
|
|
631
|
-
case "line":
|
|
632
|
-
case "arrow": {
|
|
633
|
-
const boundText = getBoundTextElement(element);
|
|
634
|
-
const maskPath = svgRoot.ownerDocument.createElementNS(SVG_NS, "mask");
|
|
635
|
-
if (boundText) {
|
|
636
|
-
maskPath.setAttribute("id", `mask-${element.id}`);
|
|
637
|
-
const maskRectVisible = svgRoot.ownerDocument.createElementNS(SVG_NS, "rect");
|
|
638
|
-
offsetX = offsetX || 0;
|
|
639
|
-
offsetY = offsetY || 0;
|
|
640
|
-
maskRectVisible.setAttribute("x", "0");
|
|
641
|
-
maskRectVisible.setAttribute("y", "0");
|
|
642
|
-
maskRectVisible.setAttribute("fill", "#fff");
|
|
643
|
-
maskRectVisible.setAttribute("width", `${element.width + 100 + offsetX}`);
|
|
644
|
-
maskRectVisible.setAttribute("height", `${element.height + 100 + offsetY}`);
|
|
645
|
-
maskPath.appendChild(maskRectVisible);
|
|
646
|
-
const maskRectInvisible = svgRoot.ownerDocument.createElementNS(SVG_NS, "rect");
|
|
647
|
-
const boundTextCoords = LinearElementEditor.getBoundTextElementPosition(element, boundText);
|
|
648
|
-
const maskX = offsetX + boundTextCoords.x - element.x;
|
|
649
|
-
const maskY = offsetY + boundTextCoords.y - element.y;
|
|
650
|
-
maskRectInvisible.setAttribute("x", maskX.toString());
|
|
651
|
-
maskRectInvisible.setAttribute("y", maskY.toString());
|
|
652
|
-
maskRectInvisible.setAttribute("fill", "#000");
|
|
653
|
-
maskRectInvisible.setAttribute("width", `${boundText.width}`);
|
|
654
|
-
maskRectInvisible.setAttribute("height", `${boundText.height}`);
|
|
655
|
-
maskRectInvisible.setAttribute("opacity", "1");
|
|
656
|
-
maskPath.appendChild(maskRectInvisible);
|
|
657
|
-
}
|
|
658
|
-
const group = svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
|
|
659
|
-
if (boundText) {
|
|
660
|
-
group.setAttribute("mask", `url(#mask-${element.id})`);
|
|
661
|
-
}
|
|
662
|
-
group.setAttribute("stroke-linecap", "round");
|
|
663
|
-
const shapes = ShapeCache.generateElementShape(element, renderConfig);
|
|
664
|
-
shapes.forEach((shape) => {
|
|
665
|
-
const node = roughSVGDrawWithPrecision(rsvg, shape, MAX_DECIMALS_FOR_SVG_EXPORT);
|
|
666
|
-
if (opacity !== 1) {
|
|
667
|
-
node.setAttribute("stroke-opacity", `${opacity}`);
|
|
668
|
-
node.setAttribute("fill-opacity", `${opacity}`);
|
|
669
|
-
}
|
|
670
|
-
node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
671
|
-
if (element.type === "line" &&
|
|
672
|
-
isPathALoop(element.points) &&
|
|
673
|
-
element.backgroundColor !== "transparent") {
|
|
674
|
-
node.setAttribute("fill-rule", "evenodd");
|
|
675
|
-
}
|
|
676
|
-
group.appendChild(node);
|
|
677
|
-
});
|
|
678
|
-
const g = maybeWrapNodesInFrameClipPath(element, root, [group, maskPath], renderConfig.frameRendering);
|
|
679
|
-
if (g) {
|
|
680
|
-
addToRoot(g, element);
|
|
681
|
-
root.appendChild(g);
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
684
|
-
addToRoot(group, element);
|
|
685
|
-
root.append(maskPath);
|
|
686
|
-
}
|
|
687
|
-
break;
|
|
688
|
-
}
|
|
689
|
-
case "freedraw": {
|
|
690
|
-
const backgroundFillShape = ShapeCache.generateElementShape(element, renderConfig);
|
|
691
|
-
const node = backgroundFillShape
|
|
692
|
-
? roughSVGDrawWithPrecision(rsvg, backgroundFillShape, MAX_DECIMALS_FOR_SVG_EXPORT)
|
|
693
|
-
: svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
|
|
694
|
-
if (opacity !== 1) {
|
|
695
|
-
node.setAttribute("stroke-opacity", `${opacity}`);
|
|
696
|
-
node.setAttribute("fill-opacity", `${opacity}`);
|
|
697
|
-
}
|
|
698
|
-
node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
699
|
-
node.setAttribute("stroke", "none");
|
|
700
|
-
const path = svgRoot.ownerDocument.createElementNS(SVG_NS, "path");
|
|
701
|
-
path.setAttribute("fill", element.strokeColor);
|
|
702
|
-
path.setAttribute("d", getFreeDrawSvgPath(element));
|
|
703
|
-
node.appendChild(path);
|
|
704
|
-
const g = maybeWrapNodesInFrameClipPath(element, root, [node], renderConfig.frameRendering);
|
|
705
|
-
addToRoot(g || node, element);
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
case "image": {
|
|
709
|
-
const width = Math.round(element.width);
|
|
710
|
-
const height = Math.round(element.height);
|
|
711
|
-
const fileData = isInitializedImageElement(element) && files[element.fileId];
|
|
712
|
-
if (fileData) {
|
|
713
|
-
const symbolId = `image-${fileData.id}`;
|
|
714
|
-
let symbol = svgRoot.querySelector(`#${symbolId}`);
|
|
715
|
-
if (!symbol) {
|
|
716
|
-
symbol = svgRoot.ownerDocument.createElementNS(SVG_NS, "symbol");
|
|
717
|
-
symbol.id = symbolId;
|
|
718
|
-
const image = svgRoot.ownerDocument.createElementNS(SVG_NS, "image");
|
|
719
|
-
image.setAttribute("width", "100%");
|
|
720
|
-
image.setAttribute("height", "100%");
|
|
721
|
-
image.setAttribute("href", fileData.dataURL);
|
|
722
|
-
symbol.appendChild(image);
|
|
723
|
-
root.prepend(symbol);
|
|
724
|
-
}
|
|
725
|
-
const use = svgRoot.ownerDocument.createElementNS(SVG_NS, "use");
|
|
726
|
-
use.setAttribute("href", `#${symbolId}`);
|
|
727
|
-
// in dark theme, revert the image color filter
|
|
728
|
-
if (renderConfig.exportWithDarkMode &&
|
|
729
|
-
fileData.mimeType !== MIME_TYPES.svg) {
|
|
730
|
-
use.setAttribute("filter", IMAGE_INVERT_FILTER);
|
|
731
|
-
}
|
|
732
|
-
use.setAttribute("width", `${width}`);
|
|
733
|
-
use.setAttribute("height", `${height}`);
|
|
734
|
-
use.setAttribute("opacity", `${opacity}`);
|
|
735
|
-
// We first apply `scale` transforms (horizontal/vertical mirroring)
|
|
736
|
-
// on the <use> element, then apply translation and rotation
|
|
737
|
-
// on the <g> element which wraps the <use>.
|
|
738
|
-
// Doing this separately is a quick hack to to work around compositing
|
|
739
|
-
// the transformations correctly (the transform-origin was not being
|
|
740
|
-
// applied correctly).
|
|
741
|
-
if (element.scale[0] !== 1 || element.scale[1] !== 1) {
|
|
742
|
-
const translateX = element.scale[0] !== 1 ? -width : 0;
|
|
743
|
-
const translateY = element.scale[1] !== 1 ? -height : 0;
|
|
744
|
-
use.setAttribute("transform", `scale(${element.scale[0]}, ${element.scale[1]}) translate(${translateX} ${translateY})`);
|
|
745
|
-
}
|
|
746
|
-
const g = svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
|
|
747
|
-
g.appendChild(use);
|
|
748
|
-
g.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
749
|
-
const clipG = maybeWrapNodesInFrameClipPath(element, root, [g], renderConfig.frameRendering);
|
|
750
|
-
addToRoot(clipG || g, element);
|
|
751
|
-
}
|
|
752
|
-
break;
|
|
753
|
-
}
|
|
754
|
-
// frames are not rendered and only acts as a container
|
|
755
|
-
case "frame":
|
|
756
|
-
case "magicframe": {
|
|
757
|
-
if (renderConfig.frameRendering.enabled &&
|
|
758
|
-
renderConfig.frameRendering.outline) {
|
|
759
|
-
const rect = document.createElementNS(SVG_NS, "rect");
|
|
760
|
-
rect.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
761
|
-
rect.setAttribute("width", `${element.width}px`);
|
|
762
|
-
rect.setAttribute("height", `${element.height}px`);
|
|
763
|
-
// Rounded corners
|
|
764
|
-
rect.setAttribute("rx", FRAME_STYLE.radius.toString());
|
|
765
|
-
rect.setAttribute("ry", FRAME_STYLE.radius.toString());
|
|
766
|
-
rect.setAttribute("fill", "none");
|
|
767
|
-
rect.setAttribute("stroke", FRAME_STYLE.strokeColor);
|
|
768
|
-
rect.setAttribute("stroke-width", FRAME_STYLE.strokeWidth.toString());
|
|
769
|
-
addToRoot(rect, element);
|
|
770
|
-
}
|
|
771
|
-
break;
|
|
772
|
-
}
|
|
773
|
-
default: {
|
|
774
|
-
if (isTextElement(element)) {
|
|
775
|
-
const node = svgRoot.ownerDocument.createElementNS(SVG_NS, "g");
|
|
776
|
-
if (opacity !== 1) {
|
|
777
|
-
node.setAttribute("stroke-opacity", `${opacity}`);
|
|
778
|
-
node.setAttribute("fill-opacity", `${opacity}`);
|
|
779
|
-
}
|
|
780
|
-
node.setAttribute("transform", `translate(${offsetX || 0} ${offsetY || 0}) rotate(${degree} ${cx} ${cy})`);
|
|
781
|
-
const lines = element.text.replace(/\r\n?/g, "\n").split("\n");
|
|
782
|
-
const lineHeightPx = getLineHeightInPx(element.fontSize, element.lineHeight);
|
|
783
|
-
const horizontalOffset = element.textAlign === "center"
|
|
784
|
-
? element.width / 2
|
|
785
|
-
: element.textAlign === "right"
|
|
786
|
-
? element.width
|
|
787
|
-
: 0;
|
|
788
|
-
const direction = isRTL(element.text) ? "rtl" : "ltr";
|
|
789
|
-
const textAnchor = element.textAlign === "center"
|
|
790
|
-
? "middle"
|
|
791
|
-
: element.textAlign === "right" || direction === "rtl"
|
|
792
|
-
? "end"
|
|
793
|
-
: "start";
|
|
794
|
-
for (let i = 0; i < lines.length; i++) {
|
|
795
|
-
const text = svgRoot.ownerDocument.createElementNS(SVG_NS, "text");
|
|
796
|
-
text.textContent = lines[i];
|
|
797
|
-
text.setAttribute("x", `${horizontalOffset}`);
|
|
798
|
-
text.setAttribute("y", `${i * lineHeightPx}`);
|
|
799
|
-
text.setAttribute("font-family", getFontFamilyString(element));
|
|
800
|
-
text.setAttribute("font-size", `${element.fontSize}px`);
|
|
801
|
-
text.setAttribute("fill", element.strokeColor);
|
|
802
|
-
text.setAttribute("text-anchor", textAnchor);
|
|
803
|
-
text.setAttribute("style", "white-space: pre;");
|
|
804
|
-
text.setAttribute("direction", direction);
|
|
805
|
-
text.setAttribute("dominant-baseline", "text-before-edge");
|
|
806
|
-
node.appendChild(text);
|
|
807
|
-
}
|
|
808
|
-
const g = maybeWrapNodesInFrameClipPath(element, root, [node], renderConfig.frameRendering);
|
|
809
|
-
addToRoot(g || node, element);
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
// @ts-ignore
|
|
813
|
-
throw new Error(`Unimplemented type ${element.type}`);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
504
|
+
context.globalAlpha = 1;
|
|
817
505
|
};
|
|
818
506
|
export const pathsCache = new WeakMap([]);
|
|
819
507
|
export function generateFreeDrawShape(element) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StaticSceneRenderConfig } from "../scene/types";
|
|
2
|
+
/** throttled to animation framerate */
|
|
3
|
+
export declare const renderStaticSceneThrottled: {
|
|
4
|
+
(config: StaticSceneRenderConfig): void;
|
|
5
|
+
flush(): void;
|
|
6
|
+
cancel(): void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Static scene is the non-ui canvas where we render elements.
|
|
10
|
+
*/
|
|
11
|
+
export declare const renderStaticScene: (renderConfig: StaticSceneRenderConfig, throttle?: boolean) => void;
|