@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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { AppState, ExcalidrawProps } from "../../types";
|
|
3
|
+
import { ElementsMap, ExcalidrawEmbeddableElement, NonDeletedExcalidrawElement } from "../../element/types";
|
|
4
|
+
import "./Hyperlink.scss";
|
|
5
|
+
export declare const Hyperlink: ({ element, elementsMap, setAppState, onLinkOpen, setToast, updateEmbedValidationStatus, }: {
|
|
6
|
+
element: NonDeletedExcalidrawElement;
|
|
7
|
+
elementsMap: ElementsMap;
|
|
8
|
+
setAppState: React.Component<any, AppState>["setState"];
|
|
9
|
+
onLinkOpen: ExcalidrawProps["onLinkOpen"];
|
|
10
|
+
setToast: (toast: {
|
|
11
|
+
message: string;
|
|
12
|
+
closable?: boolean;
|
|
13
|
+
duration?: number;
|
|
14
|
+
} | null) => void;
|
|
15
|
+
updateEmbedValidationStatus: (element: ExcalidrawEmbeddableElement, status: boolean) => void;
|
|
16
|
+
}) => JSX.Element | null;
|
|
17
|
+
export declare const getContextMenuLabel: (elements: readonly NonDeletedExcalidrawElement[], appState: AppState) => "labels.link.editEmbed" | "labels.link.edit" | "labels.link.createEmbed" | "labels.link.create";
|
|
18
|
+
export declare const showHyperlinkTooltip: (element: NonDeletedExcalidrawElement, appState: AppState, elementsMap: ElementsMap) => void;
|
|
19
|
+
export declare const hideHyperlinkToolip: () => void;
|
|
@@ -1,37 +1,32 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { getEmbedLink, embeddableURLValidator } from "
|
|
4
|
-
import { mutateElement } from "
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { t } from "../i18n";
|
|
2
|
+
import { sceneCoordsToViewportCoords, viewportCoordsToSceneCoords, wrapEvent, } from "../../utils";
|
|
3
|
+
import { getEmbedLink, embeddableURLValidator } from "../../element/embeddable";
|
|
4
|
+
import { mutateElement } from "../../element/mutateElement";
|
|
5
|
+
import { ToolButton } from "../ToolButton";
|
|
6
|
+
import { FreedrawIcon, TrashIcon } from "../icons";
|
|
7
|
+
import { t } from "../../i18n";
|
|
9
8
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState, } from "react";
|
|
10
9
|
import clsx from "clsx";
|
|
11
|
-
import { KEYS } from "
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { getElementAbsoluteCoords } from ".";
|
|
19
|
-
import { isLocalLink, normalizeLink } from "../data/url";
|
|
10
|
+
import { KEYS } from "../../keys";
|
|
11
|
+
import { EVENT, HYPERLINK_TOOLTIP_DELAY } from "../../constants";
|
|
12
|
+
import { getElementAbsoluteCoords } from "../../element/bounds";
|
|
13
|
+
import { getTooltipDiv, updateTooltipPosition } from "../Tooltip";
|
|
14
|
+
import { getSelectedElements } from "../../scene";
|
|
15
|
+
import { isPointHittingElementBoundingBox } from "../../element/collision";
|
|
16
|
+
import { isLocalLink, normalizeLink } from "../../data/url";
|
|
20
17
|
import "./Hyperlink.scss";
|
|
21
|
-
import { trackEvent } from "
|
|
22
|
-
import { useAppProps, useExcalidrawAppState } from "../
|
|
23
|
-
import { isEmbeddableElement } from "
|
|
24
|
-
import {
|
|
18
|
+
import { trackEvent } from "../../analytics";
|
|
19
|
+
import { useAppProps, useExcalidrawAppState } from "../App";
|
|
20
|
+
import { isEmbeddableElement } from "../../element/typeChecks";
|
|
21
|
+
import { getLinkHandleFromCoords } from "./helpers";
|
|
25
22
|
const CONTAINER_WIDTH = 320;
|
|
26
23
|
const SPACE_BOTTOM = 85;
|
|
27
24
|
const CONTAINER_PADDING = 5;
|
|
28
25
|
const CONTAINER_HEIGHT = 42;
|
|
29
26
|
const AUTO_HIDE_TIMEOUT = 500;
|
|
30
|
-
export const EXTERNAL_LINK_IMG = document.createElement("img");
|
|
31
|
-
EXTERNAL_LINK_IMG.src = `data:${MIME_TYPES.svg}, ${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1971c2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`)}`;
|
|
32
27
|
let IS_HYPERLINK_TOOLTIP_VISIBLE = false;
|
|
33
28
|
const embeddableLinkCache = new Map();
|
|
34
|
-
export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
29
|
+
export const Hyperlink = ({ element, elementsMap, setAppState, onLinkOpen, setToast, updateEmbedValidationStatus, }) => {
|
|
35
30
|
const appState = useExcalidrawAppState();
|
|
36
31
|
const appProps = useAppProps();
|
|
37
32
|
const linkVal = element.link || "";
|
|
@@ -52,9 +47,9 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
52
47
|
}
|
|
53
48
|
if (!link) {
|
|
54
49
|
mutateElement(element, {
|
|
55
|
-
validated: false,
|
|
56
50
|
link: null,
|
|
57
51
|
});
|
|
52
|
+
updateEmbedValidationStatus(element, false);
|
|
58
53
|
return;
|
|
59
54
|
}
|
|
60
55
|
if (!embeddableURLValidator(link, appProps.validateEmbeddable)) {
|
|
@@ -63,16 +58,18 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
63
58
|
}
|
|
64
59
|
element.link && embeddableLinkCache.set(element.id, element.link);
|
|
65
60
|
mutateElement(element, {
|
|
66
|
-
validated: false,
|
|
67
61
|
link,
|
|
68
62
|
});
|
|
69
|
-
|
|
63
|
+
updateEmbedValidationStatus(element, false);
|
|
70
64
|
}
|
|
71
65
|
else {
|
|
72
66
|
const { width, height } = element;
|
|
73
67
|
const embedLink = getEmbedLink(link);
|
|
74
|
-
if (embedLink?.
|
|
75
|
-
setToast({
|
|
68
|
+
if (embedLink?.error instanceof URIError) {
|
|
69
|
+
setToast({
|
|
70
|
+
message: t("toast.unrecognizedLinkFormat"),
|
|
71
|
+
closable: true,
|
|
72
|
+
});
|
|
76
73
|
}
|
|
77
74
|
const ar = embedLink
|
|
78
75
|
? embedLink.intrinsicSize.w / embedLink.intrinsicSize.h
|
|
@@ -93,10 +90,9 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
93
90
|
: height,
|
|
94
91
|
}
|
|
95
92
|
: {}),
|
|
96
|
-
validated: true,
|
|
97
93
|
link,
|
|
98
94
|
});
|
|
99
|
-
|
|
95
|
+
updateEmbedValidationStatus(element, true);
|
|
100
96
|
if (embeddableLinkCache.has(element.id)) {
|
|
101
97
|
embeddableLinkCache.delete(element.id);
|
|
102
98
|
}
|
|
@@ -111,6 +107,7 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
111
107
|
appProps.validateEmbeddable,
|
|
112
108
|
appState.activeEmbeddable,
|
|
113
109
|
setAppState,
|
|
110
|
+
updateEmbedValidationStatus,
|
|
114
111
|
]);
|
|
115
112
|
useLayoutEffect(() => {
|
|
116
113
|
return () => {
|
|
@@ -126,7 +123,7 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
126
123
|
if (timeoutId) {
|
|
127
124
|
clearTimeout(timeoutId);
|
|
128
125
|
}
|
|
129
|
-
const shouldHide = shouldHideLinkPopup(element, appState, [
|
|
126
|
+
const shouldHide = shouldHideLinkPopup(element, elementsMap, appState, [
|
|
130
127
|
event.clientX,
|
|
131
128
|
event.clientY,
|
|
132
129
|
]);
|
|
@@ -143,7 +140,7 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
143
140
|
clearTimeout(timeoutId);
|
|
144
141
|
}
|
|
145
142
|
};
|
|
146
|
-
}, [appState, element, isEditing, setAppState]);
|
|
143
|
+
}, [appState, element, isEditing, setAppState, elementsMap]);
|
|
147
144
|
const handleRemove = useCallback(() => {
|
|
148
145
|
trackEvent("hyperlink", "delete");
|
|
149
146
|
mutateElement(element, { link: null });
|
|
@@ -156,7 +153,7 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
156
153
|
trackEvent("hyperlink", "edit", "popup-ui");
|
|
157
154
|
setAppState({ showHyperlinkPopup: "editor" });
|
|
158
155
|
};
|
|
159
|
-
const { x, y } = getCoordsForPopover(element, appState);
|
|
156
|
+
const { x, y } = getCoordsForPopover(element, appState, elementsMap);
|
|
160
157
|
if (appState.contextMenu ||
|
|
161
158
|
appState.draggingElement ||
|
|
162
159
|
appState.resizingElement ||
|
|
@@ -197,43 +194,13 @@ export const Hyperlink = ({ element, setAppState, onLinkOpen, setToast, }) => {
|
|
|
197
194
|
}
|
|
198
195
|
}, rel: "noopener noreferrer", children: element.link })) : (_jsx("div", { className: "excalidraw-hyperlinkContainer-link", children: t("labels.link.empty") })), _jsxs("div", { className: "excalidraw-hyperlinkContainer__buttons", children: [!isEditing && (_jsx(ToolButton, { type: "button", title: t("buttons.edit"), "aria-label": t("buttons.edit"), label: t("buttons.edit"), onClick: onEdit, className: "excalidraw-hyperlinkContainer--edit", icon: FreedrawIcon })), linkVal && !isEmbeddableElement(element) && (_jsx(ToolButton, { type: "button", title: t("buttons.remove"), "aria-label": t("buttons.remove"), label: t("buttons.remove"), onClick: handleRemove, className: "excalidraw-hyperlinkContainer--remove", icon: TrashIcon }))] })] }));
|
|
199
196
|
};
|
|
200
|
-
const getCoordsForPopover = (element, appState) => {
|
|
201
|
-
const [x1, y1] = getElementAbsoluteCoords(element);
|
|
197
|
+
const getCoordsForPopover = (element, appState, elementsMap) => {
|
|
198
|
+
const [x1, y1] = getElementAbsoluteCoords(element, elementsMap);
|
|
202
199
|
const { x: viewportX, y: viewportY } = sceneCoordsToViewportCoords({ sceneX: x1 + element.width / 2, sceneY: y1 }, appState);
|
|
203
200
|
const x = viewportX - appState.offsetLeft - CONTAINER_WIDTH / 2;
|
|
204
201
|
const y = viewportY - appState.offsetTop - SPACE_BOTTOM;
|
|
205
202
|
return { x, y };
|
|
206
203
|
};
|
|
207
|
-
export const actionLink = register({
|
|
208
|
-
name: "hyperlink",
|
|
209
|
-
perform: (elements, appState) => {
|
|
210
|
-
if (appState.showHyperlinkPopup === "editor") {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
elements,
|
|
215
|
-
appState: {
|
|
216
|
-
...appState,
|
|
217
|
-
showHyperlinkPopup: "editor",
|
|
218
|
-
openMenu: null,
|
|
219
|
-
},
|
|
220
|
-
commitToHistory: true,
|
|
221
|
-
};
|
|
222
|
-
},
|
|
223
|
-
trackEvent: { category: "hyperlink", action: "click" },
|
|
224
|
-
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.K,
|
|
225
|
-
contextItemLabel: (elements, appState) => getContextMenuLabel(elements, appState),
|
|
226
|
-
predicate: (elements, appState) => {
|
|
227
|
-
const selectedElements = getSelectedElements(elements, appState);
|
|
228
|
-
return selectedElements.length === 1;
|
|
229
|
-
},
|
|
230
|
-
PanelComponent: ({ elements, appState, updateData }) => {
|
|
231
|
-
const selectedElements = getSelectedElements(elements, appState);
|
|
232
|
-
return (_jsx(ToolButton, { type: "button", icon: LinkIcon, "aria-label": t(getContextMenuLabel(elements, appState)), title: `${isEmbeddableElement(elements[0])
|
|
233
|
-
? t("labels.link.labelEmbed")
|
|
234
|
-
: t("labels.link.label")} - ${getShortcutKey("CtrlOrCmd+K")}`, onClick: () => updateData(null), selected: selectedElements.length === 1 && !!selectedElements[0].link }));
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
204
|
export const getContextMenuLabel = (elements, appState) => {
|
|
238
205
|
const selectedElements = getSelectedElements(elements, appState);
|
|
239
206
|
const label = selectedElements[0].link
|
|
@@ -245,56 +212,14 @@ export const getContextMenuLabel = (elements, appState) => {
|
|
|
245
212
|
: "labels.link.create";
|
|
246
213
|
return label;
|
|
247
214
|
};
|
|
248
|
-
export const getLinkHandleFromCoords = ([x1, y1, x2, y2], angle, appState) => {
|
|
249
|
-
const size = DEFAULT_LINK_SIZE;
|
|
250
|
-
const linkWidth = size / appState.zoom.value;
|
|
251
|
-
const linkHeight = size / appState.zoom.value;
|
|
252
|
-
const linkMarginY = size / appState.zoom.value;
|
|
253
|
-
const centerX = (x1 + x2) / 2;
|
|
254
|
-
const centerY = (y1 + y2) / 2;
|
|
255
|
-
const centeringOffset = (size - 8) / (2 * appState.zoom.value);
|
|
256
|
-
const dashedLineMargin = 4 / appState.zoom.value;
|
|
257
|
-
// Same as `ne` resize handle
|
|
258
|
-
const x = x2 + dashedLineMargin - centeringOffset;
|
|
259
|
-
const y = y1 - dashedLineMargin - linkMarginY + centeringOffset;
|
|
260
|
-
const [rotatedX, rotatedY] = rotate(x + linkWidth / 2, y + linkHeight / 2, centerX, centerY, angle);
|
|
261
|
-
return [
|
|
262
|
-
rotatedX - linkWidth / 2,
|
|
263
|
-
rotatedY - linkHeight / 2,
|
|
264
|
-
linkWidth,
|
|
265
|
-
linkHeight,
|
|
266
|
-
];
|
|
267
|
-
};
|
|
268
|
-
export const isPointHittingLinkIcon = (element, appState, [x, y]) => {
|
|
269
|
-
const threshold = 4 / appState.zoom.value;
|
|
270
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
271
|
-
const [linkX, linkY, linkWidth, linkHeight] = getLinkHandleFromCoords([x1, y1, x2, y2], element.angle, appState);
|
|
272
|
-
const hitLink = x > linkX - threshold &&
|
|
273
|
-
x < linkX + threshold + linkWidth &&
|
|
274
|
-
y > linkY - threshold &&
|
|
275
|
-
y < linkY + linkHeight + threshold;
|
|
276
|
-
return hitLink;
|
|
277
|
-
};
|
|
278
|
-
export const isPointHittingLink = (element, appState, [x, y], isMobile) => {
|
|
279
|
-
if (!element.link || appState.selectedElementIds[element.id]) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
const threshold = 4 / appState.zoom.value;
|
|
283
|
-
if (!isMobile &&
|
|
284
|
-
appState.viewModeEnabled &&
|
|
285
|
-
isPointHittingElementBoundingBox(element, [x, y], threshold, null)) {
|
|
286
|
-
return true;
|
|
287
|
-
}
|
|
288
|
-
return isPointHittingLinkIcon(element, appState, [x, y]);
|
|
289
|
-
};
|
|
290
215
|
let HYPERLINK_TOOLTIP_TIMEOUT_ID = null;
|
|
291
|
-
export const showHyperlinkTooltip = (element, appState) => {
|
|
216
|
+
export const showHyperlinkTooltip = (element, appState, elementsMap) => {
|
|
292
217
|
if (HYPERLINK_TOOLTIP_TIMEOUT_ID) {
|
|
293
218
|
clearTimeout(HYPERLINK_TOOLTIP_TIMEOUT_ID);
|
|
294
219
|
}
|
|
295
|
-
HYPERLINK_TOOLTIP_TIMEOUT_ID = window.setTimeout(() => renderTooltip(element, appState), HYPERLINK_TOOLTIP_DELAY);
|
|
220
|
+
HYPERLINK_TOOLTIP_TIMEOUT_ID = window.setTimeout(() => renderTooltip(element, appState, elementsMap), HYPERLINK_TOOLTIP_DELAY);
|
|
296
221
|
};
|
|
297
|
-
const renderTooltip = (element, appState) => {
|
|
222
|
+
const renderTooltip = (element, appState, elementsMap) => {
|
|
298
223
|
if (!element.link) {
|
|
299
224
|
return;
|
|
300
225
|
}
|
|
@@ -302,7 +227,7 @@ const renderTooltip = (element, appState) => {
|
|
|
302
227
|
tooltipDiv.classList.add("excalidraw-tooltip--visible");
|
|
303
228
|
tooltipDiv.style.maxWidth = "20rem";
|
|
304
229
|
tooltipDiv.textContent = element.link;
|
|
305
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
230
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
306
231
|
const [linkX, linkY, linkWidth, linkHeight] = getLinkHandleFromCoords([x1, y1, x2, y2], element.angle, appState);
|
|
307
232
|
const linkViewportCoords = sceneCoordsToViewportCoords({ sceneX: linkX, sceneY: linkY }, appState);
|
|
308
233
|
updateTooltipPosition(tooltipDiv, {
|
|
@@ -323,14 +248,14 @@ export const hideHyperlinkToolip = () => {
|
|
|
323
248
|
getTooltipDiv().classList.remove("excalidraw-tooltip--visible");
|
|
324
249
|
}
|
|
325
250
|
};
|
|
326
|
-
|
|
251
|
+
const shouldHideLinkPopup = (element, elementsMap, appState, [clientX, clientY]) => {
|
|
327
252
|
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords({ clientX, clientY }, appState);
|
|
328
253
|
const threshold = 15 / appState.zoom.value;
|
|
329
254
|
// hitbox to prevent hiding when hovered in element bounding box
|
|
330
|
-
if (isPointHittingElementBoundingBox(element, [sceneX, sceneY], threshold, null)) {
|
|
255
|
+
if (isPointHittingElementBoundingBox(element, elementsMap, [sceneX, sceneY], threshold, null)) {
|
|
331
256
|
return false;
|
|
332
257
|
}
|
|
333
|
-
const [x1, y1, x2] = getElementAbsoluteCoords(element);
|
|
258
|
+
const [x1, y1, x2] = getElementAbsoluteCoords(element, elementsMap);
|
|
334
259
|
// hit box to prevent hiding when hovered in the vertical area between element and popover
|
|
335
260
|
if (sceneX >= x1 &&
|
|
336
261
|
sceneX <= x2 &&
|
|
@@ -339,7 +264,7 @@ export const shouldHideLinkPopup = (element, appState, [clientX, clientY]) => {
|
|
|
339
264
|
return false;
|
|
340
265
|
}
|
|
341
266
|
// hit box to prevent hiding when hovered around popover within threshold
|
|
342
|
-
const { x: popoverX, y: popoverY } = getCoordsForPopover(element, appState);
|
|
267
|
+
const { x: popoverX, y: popoverY } = getCoordsForPopover(element, appState, elementsMap);
|
|
343
268
|
if (clientX >= popoverX - threshold &&
|
|
344
269
|
clientX <= popoverX + CONTAINER_WIDTH + CONTAINER_PADDING * 2 + threshold &&
|
|
345
270
|
clientY >= popoverY - threshold &&
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Bounds } from "../../element/bounds";
|
|
2
|
+
import { ElementsMap, NonDeletedExcalidrawElement } from "../../element/types";
|
|
3
|
+
import { AppState, UIAppState } from "../../types";
|
|
4
|
+
export declare const EXTERNAL_LINK_IMG: HTMLImageElement;
|
|
5
|
+
export declare const getLinkHandleFromCoords: ([x1, y1, x2, y2]: Bounds, angle: number, appState: Pick<UIAppState, "zoom">) => Bounds;
|
|
6
|
+
export declare const isPointHittingLinkIcon: (element: NonDeletedExcalidrawElement, elementsMap: ElementsMap, appState: AppState, [x, y]: readonly [number, number]) => boolean;
|
|
7
|
+
export declare const isPointHittingLink: (element: NonDeletedExcalidrawElement, elementsMap: ElementsMap, appState: AppState, [x, y]: readonly [number, number], isMobile: boolean) => boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MIME_TYPES } from "../../constants";
|
|
2
|
+
import { getElementAbsoluteCoords } from "../../element/bounds";
|
|
3
|
+
import { isPointHittingElementBoundingBox } from "../../element/collision";
|
|
4
|
+
import { rotate } from "../../math";
|
|
5
|
+
import { DEFAULT_LINK_SIZE } from "../../renderer/renderElement";
|
|
6
|
+
export const EXTERNAL_LINK_IMG = document.createElement("img");
|
|
7
|
+
EXTERNAL_LINK_IMG.src = `data:${MIME_TYPES.svg}, ${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1971c2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`)}`;
|
|
8
|
+
export const getLinkHandleFromCoords = ([x1, y1, x2, y2], angle, appState) => {
|
|
9
|
+
const size = DEFAULT_LINK_SIZE;
|
|
10
|
+
const linkWidth = size / appState.zoom.value;
|
|
11
|
+
const linkHeight = size / appState.zoom.value;
|
|
12
|
+
const linkMarginY = size / appState.zoom.value;
|
|
13
|
+
const centerX = (x1 + x2) / 2;
|
|
14
|
+
const centerY = (y1 + y2) / 2;
|
|
15
|
+
const centeringOffset = (size - 8) / (2 * appState.zoom.value);
|
|
16
|
+
const dashedLineMargin = 4 / appState.zoom.value;
|
|
17
|
+
// Same as `ne` resize handle
|
|
18
|
+
const x = x2 + dashedLineMargin - centeringOffset;
|
|
19
|
+
const y = y1 - dashedLineMargin - linkMarginY + centeringOffset;
|
|
20
|
+
const [rotatedX, rotatedY] = rotate(x + linkWidth / 2, y + linkHeight / 2, centerX, centerY, angle);
|
|
21
|
+
return [
|
|
22
|
+
rotatedX - linkWidth / 2,
|
|
23
|
+
rotatedY - linkHeight / 2,
|
|
24
|
+
linkWidth,
|
|
25
|
+
linkHeight,
|
|
26
|
+
];
|
|
27
|
+
};
|
|
28
|
+
export const isPointHittingLinkIcon = (element, elementsMap, appState, [x, y]) => {
|
|
29
|
+
const threshold = 4 / appState.zoom.value;
|
|
30
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
31
|
+
const [linkX, linkY, linkWidth, linkHeight] = getLinkHandleFromCoords([x1, y1, x2, y2], element.angle, appState);
|
|
32
|
+
const hitLink = x > linkX - threshold &&
|
|
33
|
+
x < linkX + threshold + linkWidth &&
|
|
34
|
+
y > linkY - threshold &&
|
|
35
|
+
y < linkY + linkHeight + threshold;
|
|
36
|
+
return hitLink;
|
|
37
|
+
};
|
|
38
|
+
export const isPointHittingLink = (element, elementsMap, appState, [x, y], isMobile) => {
|
|
39
|
+
if (!element.link || appState.selectedElementIds[element.id]) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const threshold = 4 / appState.zoom.value;
|
|
43
|
+
if (!isMobile &&
|
|
44
|
+
appState.viewModeEnabled &&
|
|
45
|
+
isPointHittingElementBoundingBox(element, elementsMap, [x, y], threshold, null)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return isPointHittingLinkIcon(element, elementsMap, appState, [x, y]);
|
|
49
|
+
};
|
|
@@ -42,7 +42,7 @@ export declare const HelpIcon: JSX.Element;
|
|
|
42
42
|
export declare const ExternalLinkIcon: JSX.Element;
|
|
43
43
|
export declare const GithubIcon: JSX.Element;
|
|
44
44
|
export declare const DiscordIcon: JSX.Element;
|
|
45
|
-
export declare const
|
|
45
|
+
export declare const XBrandIcon: JSX.Element;
|
|
46
46
|
export declare const checkIcon: JSX.Element;
|
|
47
47
|
export declare const LinkIcon: JSX.Element;
|
|
48
48
|
export declare const save: JSX.Element;
|
|
@@ -60,6 +60,7 @@ export declare const UndoIcon: JSX.Element;
|
|
|
60
60
|
export declare const RedoIcon: JSX.Element;
|
|
61
61
|
export declare const questionCircle: JSX.Element;
|
|
62
62
|
export declare const share: JSX.Element;
|
|
63
|
+
export declare const warning: JSX.Element;
|
|
63
64
|
export declare const shareIOS: JSX.Element;
|
|
64
65
|
export declare const shareWindows: JSX.Element;
|
|
65
66
|
export declare const resetZoom: JSX.Element;
|
|
@@ -89,7 +89,7 @@ export const HelpIcon = createIcon(_jsxs("g", { strokeWidth: "1.5", children: [_
|
|
|
89
89
|
export const ExternalLinkIcon = createIcon(_jsx("path", { strokeWidth: "1.25", d: "M9.167 5.833H5.833c-1.254 0-2.5 1.282-2.5 2.5v5.834c0 1.283 1.252 2.5 2.5 2.5h5.834c1.251 0 2.5-1.25 2.5-2.5v-3.334M8.333 11.667l8.334-8.334M12.5 3.333h4.167V7.5" }), modifiedTablerIconProps);
|
|
90
90
|
export const GithubIcon = createIcon(_jsx("path", { d: "M7.5 15.833c-3.583 1.167-3.583-2.083-5-2.5m10 4.167v-2.917c0-.833.083-1.166-.417-1.666 2.334-.25 4.584-1.167 4.584-5a3.833 3.833 0 0 0-1.084-2.667 3.5 3.5 0 0 0-.083-2.667s-.917-.25-2.917 1.084a10.25 10.25 0 0 0-5.166 0C5.417 2.333 4.5 2.583 4.5 2.583a3.5 3.5 0 0 0-.083 2.667 3.833 3.833 0 0 0-1.084 2.667c0 3.833 2.25 4.75 4.584 5-.5.5-.5 1-.417 1.666V17.5", strokeWidth: "1.25" }), modifiedTablerIconProps);
|
|
91
91
|
export const DiscordIcon = createIcon(_jsxs("g", { strokeWidth: "1.25", children: [_jsx("path", { d: "M7.5 10.833a.833.833 0 1 0 0-1.666.833.833 0 0 0 0 1.666ZM12.5 10.833a.833.833 0 1 0 0-1.666.833.833 0 0 0 0 1.666ZM6.25 6.25c2.917-.833 4.583-.833 7.5 0M5.833 13.75c2.917.833 5.417.833 8.334 0" }), _jsx("path", { d: "M12.917 14.167c0 .833 1.25 2.5 1.666 2.5 1.25 0 2.361-1.39 2.917-2.5.556-1.39.417-4.861-1.25-9.584-1.214-.846-2.5-1.116-3.75-1.25l-.833 2.084M7.083 14.167c0 .833-1.13 2.5-1.526 2.5-1.191 0-2.249-1.39-2.778-2.5-.529-1.39-.397-4.861 1.19-9.584 1.157-.846 2.318-1.116 3.531-1.25l.833 2.084" })] }), modifiedTablerIconProps);
|
|
92
|
-
export const
|
|
92
|
+
export const XBrandIcon = createIcon(_jsxs("g", { strokeWidth: "1.25", children: [_jsx("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), _jsx("path", { d: "M4 4l11.733 16h4.267l-11.733 -16z" }), _jsx("path", { d: "M4 20l6.768 -6.768m2.46 -2.46l6.772 -6.772" })] }), tablerIconProps);
|
|
93
93
|
export const checkIcon = createIcon(_jsx("polyline", { fill: "none", stroke: "currentColor", points: "20 6 9 17 4 12" }), {
|
|
94
94
|
width: 24,
|
|
95
95
|
height: 24,
|
|
@@ -111,6 +111,7 @@ export const UndoIcon = createIcon(_jsx("path", { d: "M7.5 10.833 4.167 7.5 7.5
|
|
|
111
111
|
export const RedoIcon = createIcon(_jsx("path", { d: "M12.5 10.833 15.833 7.5 12.5 4.167M15.833 7.5H6.667a3.333 3.333 0 1 0 0 6.667H7.5", strokeWidth: "1.25" }), modifiedTablerIconProps);
|
|
112
112
|
export const questionCircle = createIcon("M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z", { mirror: true });
|
|
113
113
|
export const share = createIcon(_jsx("path", { d: "M5 12.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM15 7.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM15 17.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM7.25 8.917l5.5-2.834M7.25 11.083l5.5 2.834", strokeWidth: "1.5" }), modifiedTablerIconProps);
|
|
114
|
+
export const warning = createIcon("M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z");
|
|
114
115
|
export const shareIOS = createIcon("M16 5l-1.42 1.42-1.59-1.59V16h-1.98V4.83L9.42 6.42 8 5l4-4 4 4zm4 5v11c0 1.1-.9 2-2 2H6c-1.11 0-2-.9-2-2V10c0-1.11.89-2 2-2h3v2H6v11h12V10h-3V8h3c1.1 0 2 .89 2 2z", { width: 24, height: 24 });
|
|
115
116
|
export const shareWindows = createIcon(_jsxs(_Fragment, { children: [_jsx("path", { fill: "currentColor", d: "M40 5.6v6.1l-4.1.7c-8.9 1.4-16.5 6.9-20.6 15C13 32 10.9 43 12.4 43c.4 0 2.4-1.3 4.4-3 5-3.9 12.1-7 18.2-7.7l5-.6v12.8l11.2-11.3L62.5 22 51.2 10.8 40-.5v6.1zm10.2 22.6L44 34.5v-6.8l-6.9.6c-3.9.3-9.8 1.7-13.2 3.1-3.5 1.4-6.5 2.4-6.7 2.2-.9-1 3-7.5 6.4-10.8C28 18.6 34.4 16 40.1 16c3.7 0 3.9-.1 3.9-3.2V9.5l6.2 6.3 6.3 6.2-6.3 6.2z" }), _jsx("path", { stroke: "currentColor", fill: "currentColor", d: "M0 36v20h48v-6.2c0-6 0-6.1-2-4.3-1.1 1-2 2.9-2 4.2V52H4V34c0-17.3-.1-18-2-18s-2 .7-2 20z" })] }), { width: 64, height: 64 });
|
|
116
117
|
// Icon imported form Storybook
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { t } from "../../i18n";
|
|
3
|
-
import {
|
|
3
|
+
import { share } from "../icons";
|
|
4
4
|
import { Button } from "../Button";
|
|
5
5
|
import clsx from "clsx";
|
|
6
6
|
import "./LiveCollaborationTrigger.scss";
|
|
7
7
|
import { useUIAppState } from "../../context/ui-appState";
|
|
8
8
|
const LiveCollaborationTrigger = ({ isCollaborating, onSelect, ...rest }) => {
|
|
9
9
|
const appState = useUIAppState();
|
|
10
|
-
|
|
10
|
+
const showIconOnly = appState.width < 830;
|
|
11
|
+
return (_jsxs(Button, { ...rest, className: clsx("collab-button", { active: isCollaborating }), type: "button", onSelect: onSelect, style: { position: "relative", width: showIconOnly ? undefined : "auto" }, title: t("labels.liveCollaboration"), children: [showIconOnly ? share : t("labels.share"), appState.collaborators.size > 0 && (_jsx("div", { className: "CollabButton-collaborators", children: appState.collaborators.size }))] }));
|
|
11
12
|
};
|
|
12
13
|
export default LiveCollaborationTrigger;
|
|
13
14
|
LiveCollaborationTrigger.displayName = "LiveCollaborationTrigger";
|
|
@@ -3,7 +3,7 @@ import { getShortcutFromShortcutName } from "../../actions/shortcuts";
|
|
|
3
3
|
import { useI18n } from "../../i18n";
|
|
4
4
|
import { useExcalidrawSetAppState, useExcalidrawActionManager, useExcalidrawElements, useAppProps, } from "../App";
|
|
5
5
|
import { ExportIcon, ExportImageIcon, HelpIcon, LoadIcon, MoonIcon, save, SunIcon, TrashIcon, usersIcon, } from "../icons";
|
|
6
|
-
import { GithubIcon, DiscordIcon,
|
|
6
|
+
import { GithubIcon, DiscordIcon, XBrandIcon } from "../icons";
|
|
7
7
|
import DropdownMenuItem from "../dropdownMenu/DropdownMenuItem";
|
|
8
8
|
import DropdownMenuItemLink from "../dropdownMenu/DropdownMenuItemLink";
|
|
9
9
|
import { actionClearCanvas, actionLoadScene, actionSaveToActiveFile, actionShortcuts, actionToggleTheme, } from "../../actions";
|
|
@@ -105,7 +105,10 @@ export const Export = () => {
|
|
|
105
105
|
}, "data-testid": "json-export-button", "aria-label": t("buttons.export"), children: t("buttons.export") }));
|
|
106
106
|
};
|
|
107
107
|
Export.displayName = "Export";
|
|
108
|
-
export const Socials = () =>
|
|
108
|
+
export const Socials = () => {
|
|
109
|
+
const { t } = useI18n();
|
|
110
|
+
return (_jsxs(_Fragment, { children: [_jsx(DropdownMenuItemLink, { icon: GithubIcon, href: "https://github.com/excalidraw/excalidraw", "aria-label": "GitHub", children: "GitHub" }), _jsx(DropdownMenuItemLink, { icon: XBrandIcon, href: "https://x.com/excalidraw", "aria-label": "X", children: t("labels.followUs") }), _jsx(DropdownMenuItemLink, { icon: DiscordIcon, href: "https://discord.gg/UexuTaE", "aria-label": "Discord", children: t("labels.discordChat") })] }));
|
|
111
|
+
};
|
|
109
112
|
Socials.displayName = "Socials";
|
|
110
113
|
export const LiveCollaborationTrigger = ({ onSelect, isCollaborating, }) => {
|
|
111
114
|
const { t } = useI18n();
|
|
@@ -6,6 +6,7 @@ export declare const isAndroid: boolean;
|
|
|
6
6
|
export declare const isFirefox: boolean;
|
|
7
7
|
export declare const isChrome: boolean;
|
|
8
8
|
export declare const isSafari: boolean;
|
|
9
|
+
export declare const isIOS: boolean;
|
|
9
10
|
export declare const isBrave: () => boolean;
|
|
10
11
|
export declare const APP_NAME = "Excalidraw";
|
|
11
12
|
export declare const DRAGGING_THRESHOLD = 10;
|
|
@@ -28,6 +29,7 @@ export declare const POINTER_BUTTON: {
|
|
|
28
29
|
readonly WHEEL: 1;
|
|
29
30
|
readonly SECONDARY: 2;
|
|
30
31
|
readonly TOUCH: -1;
|
|
32
|
+
readonly ERASER: 5;
|
|
31
33
|
};
|
|
32
34
|
export declare const POINTER_EVENTS: {
|
|
33
35
|
readonly enabled: "all";
|
|
@@ -116,6 +118,7 @@ export declare const DEFAULT_FONT_FAMILY: FontFamilyValues;
|
|
|
116
118
|
export declare const DEFAULT_TEXT_ALIGN = "left";
|
|
117
119
|
export declare const DEFAULT_VERTICAL_ALIGN = "top";
|
|
118
120
|
export declare const DEFAULT_VERSION = "{version}";
|
|
121
|
+
export declare const DEFAULT_TRANSFORM_HANDLE_SPACING = 2;
|
|
119
122
|
export declare const CANVAS_ONLY_ACTIONS: string[];
|
|
120
123
|
export declare const GRID_SIZE = 20;
|
|
121
124
|
export declare const IMAGE_MIME_TYPES: {
|
|
@@ -266,3 +269,8 @@ export declare const EDITOR_LS_KEYS: {
|
|
|
266
269
|
readonly MERMAID_TO_EXCALIDRAW: "mermaid-to-excalidraw";
|
|
267
270
|
readonly PUBLISH_LIBRARY: "publish-library-data";
|
|
268
271
|
};
|
|
272
|
+
/**
|
|
273
|
+
* not translated as this is used only in public, stateless API as default value
|
|
274
|
+
* where filename is optional and we can't retrieve name from app state
|
|
275
|
+
*/
|
|
276
|
+
export declare const DEFAULT_FILENAME = "Untitled";
|
|
@@ -8,6 +8,9 @@ export const isFirefox = "netscape" in window &&
|
|
|
8
8
|
navigator.userAgent.indexOf("Gecko") > 1;
|
|
9
9
|
export const isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
|
|
10
10
|
export const isSafari = !isChrome && navigator.userAgent.indexOf("Safari") !== -1;
|
|
11
|
+
export const isIOS = /iPad|iPhone/.test(navigator.platform) ||
|
|
12
|
+
// iPadOS 13+
|
|
13
|
+
(navigator.userAgent.includes("Mac") && "ontouchend" in document);
|
|
11
14
|
// keeping function so it can be mocked in test
|
|
12
15
|
export const isBrave = () => navigator.brave?.isBrave?.name === "isBrave";
|
|
13
16
|
export const APP_NAME = "Excalidraw";
|
|
@@ -31,6 +34,7 @@ export const POINTER_BUTTON = {
|
|
|
31
34
|
WHEEL: 1,
|
|
32
35
|
SECONDARY: 2,
|
|
33
36
|
TOUCH: -1,
|
|
37
|
+
ERASER: 5,
|
|
34
38
|
};
|
|
35
39
|
export const POINTER_EVENTS = {
|
|
36
40
|
enabled: "all",
|
|
@@ -121,6 +125,7 @@ export const DEFAULT_FONT_FAMILY = FONT_FAMILY.Virgil;
|
|
|
121
125
|
export const DEFAULT_TEXT_ALIGN = "left";
|
|
122
126
|
export const DEFAULT_VERTICAL_ALIGN = "top";
|
|
123
127
|
export const DEFAULT_VERSION = "{version}";
|
|
128
|
+
export const DEFAULT_TRANSFORM_HANDLE_SPACING = 2;
|
|
124
129
|
export const CANVAS_ONLY_ACTIONS = ["selectAll"];
|
|
125
130
|
export const GRID_SIZE = 20; // TODO make it configurable?
|
|
126
131
|
export const IMAGE_MIME_TYPES = {
|
|
@@ -310,3 +315,8 @@ export const EDITOR_LS_KEYS = {
|
|
|
310
315
|
MERMAID_TO_EXCALIDRAW: "mermaid-to-excalidraw",
|
|
311
316
|
PUBLISH_LIBRARY: "publish-library-data",
|
|
312
317
|
};
|
|
318
|
+
/**
|
|
319
|
+
* not translated as this is used only in public, stateless API as default value
|
|
320
|
+
* where filename is optional and we can't retrieve name from app state
|
|
321
|
+
*/
|
|
322
|
+
export const DEFAULT_FILENAME = "Untitled";
|
|
@@ -3,7 +3,6 @@ import { cleanAppStateForExport } from "../appState";
|
|
|
3
3
|
import { IMAGE_MIME_TYPES, MIME_TYPES } from "../constants";
|
|
4
4
|
import { clearElementsForExport } from "../element";
|
|
5
5
|
import { CanvasError, ImageSceneDataError } from "../errors";
|
|
6
|
-
import { t } from "../i18n";
|
|
7
6
|
import { calculateScrollCenter } from "../scene";
|
|
8
7
|
import { bytesToHexString, isPromiseLike } from "../utils";
|
|
9
8
|
import { nativeFileSystemSupported } from "./filesystem";
|
|
@@ -17,10 +16,10 @@ const parseFileContents = async (blob) => {
|
|
|
17
16
|
}
|
|
18
17
|
catch (error) {
|
|
19
18
|
if (error.message === "INVALID") {
|
|
20
|
-
throw new ImageSceneDataError(t
|
|
19
|
+
throw new ImageSceneDataError("Image doesn't contain scene", "IMAGE_NOT_CONTAINS_SCENE_DATA");
|
|
21
20
|
}
|
|
22
21
|
else {
|
|
23
|
-
throw new ImageSceneDataError(
|
|
22
|
+
throw new ImageSceneDataError("Error: cannot restore image");
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
}
|
|
@@ -47,10 +46,10 @@ const parseFileContents = async (blob) => {
|
|
|
47
46
|
}
|
|
48
47
|
catch (error) {
|
|
49
48
|
if (error.message === "INVALID") {
|
|
50
|
-
throw new ImageSceneDataError(t
|
|
49
|
+
throw new ImageSceneDataError("Image doesn't contain scene", "IMAGE_NOT_CONTAINS_SCENE_DATA");
|
|
51
50
|
}
|
|
52
51
|
else {
|
|
53
|
-
throw new ImageSceneDataError(
|
|
52
|
+
throw new ImageSceneDataError("Error: cannot restore image");
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
}
|
|
@@ -112,7 +111,7 @@ fileHandle) => {
|
|
|
112
111
|
}
|
|
113
112
|
catch (error) {
|
|
114
113
|
if (isSupportedImageFile(blob)) {
|
|
115
|
-
throw new ImageSceneDataError(t
|
|
114
|
+
throw new ImageSceneDataError("Image doesn't contain scene", "IMAGE_NOT_CONTAINS_SCENE_DATA");
|
|
116
115
|
}
|
|
117
116
|
throw error;
|
|
118
117
|
}
|
|
@@ -139,13 +138,13 @@ fileHandle) => {
|
|
|
139
138
|
data,
|
|
140
139
|
};
|
|
141
140
|
}
|
|
142
|
-
throw new Error(
|
|
141
|
+
throw new Error("Error: invalid file");
|
|
143
142
|
}
|
|
144
143
|
catch (error) {
|
|
145
144
|
if (error instanceof ImageSceneDataError) {
|
|
146
145
|
throw error;
|
|
147
146
|
}
|
|
148
|
-
throw new Error(
|
|
147
|
+
throw new Error("Error: invalid file");
|
|
149
148
|
}
|
|
150
149
|
};
|
|
151
150
|
export const loadFromBlob = async (blob,
|
|
@@ -155,7 +154,7 @@ localAppState, localElements,
|
|
|
155
154
|
fileHandle) => {
|
|
156
155
|
const ret = await loadSceneOrLibraryFromBlob(blob, localAppState, localElements, fileHandle);
|
|
157
156
|
if (ret.type !== MIME_TYPES.excalidraw) {
|
|
158
|
-
throw new Error(
|
|
157
|
+
throw new Error("Error: invalid file");
|
|
159
158
|
}
|
|
160
159
|
return ret.data;
|
|
161
160
|
};
|
|
@@ -178,7 +177,7 @@ export const canvasToBlob = async (canvas) => {
|
|
|
178
177
|
}
|
|
179
178
|
canvas.toBlob((blob) => {
|
|
180
179
|
if (!blob) {
|
|
181
|
-
return reject(new CanvasError(
|
|
180
|
+
return reject(new CanvasError("Error: Canvas too big", "CANVAS_POSSIBLY_TOO_BIG"));
|
|
182
181
|
}
|
|
183
182
|
resolve(blob);
|
|
184
183
|
});
|
|
@@ -249,7 +248,7 @@ export const resizeImageFile = async (file, opts) => {
|
|
|
249
248
|
};
|
|
250
249
|
}
|
|
251
250
|
if (!isSupportedImageFile(file)) {
|
|
252
|
-
throw new Error(
|
|
251
|
+
throw new Error("Error: unsupported file type", { cause: "UNSUPPORTED" });
|
|
253
252
|
}
|
|
254
253
|
return new File([await reduce.toBlob(file, { max: opts.maxWidthOrHeight })], file.name, {
|
|
255
254
|
type: opts.outputType || file.type,
|
|
@@ -266,17 +265,17 @@ export const ImageURLToFile = async (imageUrl, filename = "") => {
|
|
|
266
265
|
response = await fetch(imageUrl);
|
|
267
266
|
}
|
|
268
267
|
catch (error) {
|
|
269
|
-
throw new Error(
|
|
268
|
+
throw new Error("Error: failed to fetch image", { cause: "FETCH_ERROR" });
|
|
270
269
|
}
|
|
271
270
|
if (!response.ok) {
|
|
272
|
-
throw new Error(
|
|
271
|
+
throw new Error("Error: failed to fetch image", { cause: "FETCH_ERROR" });
|
|
273
272
|
}
|
|
274
273
|
const blob = await response.blob();
|
|
275
274
|
if (blob.type && isSupportedImageFile(blob)) {
|
|
276
275
|
const name = filename || blob.name || "";
|
|
277
276
|
return new File([blob], name, { type: blob.type });
|
|
278
277
|
}
|
|
279
|
-
throw new Error(
|
|
278
|
+
throw new Error("Error: unsupported file type", { cause: "UNSUPPORTED" });
|
|
280
279
|
};
|
|
281
280
|
export const getFileFromEvent = async (event) => {
|
|
282
281
|
const file = event.dataTransfer.files.item(0);
|
|
@@ -6,7 +6,7 @@ export declare const fileOpen: <M extends boolean | undefined = false>(opts: {
|
|
|
6
6
|
description: string;
|
|
7
7
|
multiple?: M | undefined;
|
|
8
8
|
}) => Promise<M extends false | undefined ? File : File[]>;
|
|
9
|
-
export declare const fileSave: (blob: Blob
|
|
9
|
+
export declare const fileSave: (blob: Blob | Promise<Blob>, opts: {
|
|
10
10
|
/** supply without the extension */
|
|
11
11
|
name: string;
|
|
12
12
|
/** file extension */
|
|
@@ -15,7 +15,8 @@ export declare const exportCanvas: (type: Omit<ExportType, "backend">, elements:
|
|
|
15
15
|
exportBackground: boolean;
|
|
16
16
|
exportPadding?: number | undefined;
|
|
17
17
|
viewBackgroundColor: string;
|
|
18
|
-
|
|
18
|
+
/** filename, if applicable */
|
|
19
|
+
name?: string | undefined;
|
|
19
20
|
fileHandle?: FileSystemHandle | null | undefined;
|
|
20
21
|
exportingFrame: ExcalidrawFrameLikeElement | null;
|
|
21
22
|
}) => Promise<FileSystemHandle | null | undefined>;
|