mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
fix: decouple pure functions from hyperlink to prevent mermaid bundling (#7710)
* move hyperlink code into its folder * move pure js functions to hyperlink/helpers and move actionLink to actions * fix tests * fix
This commit is contained in:
parent
79d9dc2f8f
commit
2e719ff671
9 changed files with 198 additions and 176 deletions
54
packages/excalidraw/actions/actionLink.tsx
Normal file
54
packages/excalidraw/actions/actionLink.tsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { getContextMenuLabel } from "../components/hyperlink/Hyperlink";
|
||||||
|
import { LinkIcon } from "../components/icons";
|
||||||
|
import { ToolButton } from "../components/ToolButton";
|
||||||
|
import { isEmbeddableElement } from "../element/typeChecks";
|
||||||
|
import { t } from "../i18n";
|
||||||
|
import { KEYS } from "../keys";
|
||||||
|
import { getSelectedElements } from "../scene";
|
||||||
|
import { getShortcutKey } from "../utils";
|
||||||
|
import { register } from "./register";
|
||||||
|
|
||||||
|
export const actionLink = register({
|
||||||
|
name: "hyperlink",
|
||||||
|
perform: (elements, appState) => {
|
||||||
|
if (appState.showHyperlinkPopup === "editor") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
elements,
|
||||||
|
appState: {
|
||||||
|
...appState,
|
||||||
|
showHyperlinkPopup: "editor",
|
||||||
|
openMenu: null,
|
||||||
|
},
|
||||||
|
commitToHistory: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
trackEvent: { category: "hyperlink", action: "click" },
|
||||||
|
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.K,
|
||||||
|
contextItemLabel: (elements, appState) =>
|
||||||
|
getContextMenuLabel(elements, appState),
|
||||||
|
predicate: (elements, appState) => {
|
||||||
|
const selectedElements = getSelectedElements(elements, appState);
|
||||||
|
return selectedElements.length === 1;
|
||||||
|
},
|
||||||
|
PanelComponent: ({ elements, appState, updateData }) => {
|
||||||
|
const selectedElements = getSelectedElements(elements, appState);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolButton
|
||||||
|
type="button"
|
||||||
|
icon={LinkIcon}
|
||||||
|
aria-label={t(getContextMenuLabel(elements, appState))}
|
||||||
|
title={`${
|
||||||
|
isEmbeddableElement(elements[0])
|
||||||
|
? t("labels.link.labelEmbed")
|
||||||
|
: t("labels.link.label")
|
||||||
|
} - ${getShortcutKey("CtrlOrCmd+K")}`}
|
||||||
|
onClick={() => updateData(null)}
|
||||||
|
selected={selectedElements.length === 1 && !!selectedElements[0].link}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -83,6 +83,6 @@ export { actionToggleObjectsSnapMode } from "./actionToggleObjectsSnapMode";
|
||||||
|
|
||||||
export { actionToggleStats } from "./actionToggleStats";
|
export { actionToggleStats } from "./actionToggleStats";
|
||||||
export { actionUnbindText, actionBindText } from "./actionBoundText";
|
export { actionUnbindText, actionBindText } from "./actionBoundText";
|
||||||
export { actionLink } from "../element/Hyperlink";
|
export { actionLink } from "./actionLink";
|
||||||
export { actionToggleElementLock } from "./actionElementLock";
|
export { actionToggleElementLock } from "./actionElementLock";
|
||||||
export { actionToggleLinearEditor } from "./actionLinearEditor";
|
export { actionToggleLinearEditor } from "./actionLinearEditor";
|
||||||
|
|
|
@ -326,9 +326,7 @@ import {
|
||||||
showHyperlinkTooltip,
|
showHyperlinkTooltip,
|
||||||
hideHyperlinkToolip,
|
hideHyperlinkToolip,
|
||||||
Hyperlink,
|
Hyperlink,
|
||||||
isPointHittingLink,
|
} from "../components/hyperlink/Hyperlink";
|
||||||
isPointHittingLinkIcon,
|
|
||||||
} from "../element/Hyperlink";
|
|
||||||
import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
|
import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
|
||||||
import { shouldShowBoundingBox } from "../element/transformHandles";
|
import { shouldShowBoundingBox } from "../element/transformHandles";
|
||||||
import { actionUnlockAllElements } from "../actions/actionElementLock";
|
import { actionUnlockAllElements } from "../actions/actionElementLock";
|
||||||
|
@ -410,6 +408,10 @@ import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
|
||||||
import { getRenderOpacity } from "../renderer/renderElement";
|
import { getRenderOpacity } from "../renderer/renderElement";
|
||||||
import { textWysiwyg } from "../element/textWysiwyg";
|
import { textWysiwyg } from "../element/textWysiwyg";
|
||||||
import { isOverScrollBars } from "../scene/scrollbars";
|
import { isOverScrollBars } from "../scene/scrollbars";
|
||||||
|
import {
|
||||||
|
isPointHittingLink,
|
||||||
|
isPointHittingLinkIcon,
|
||||||
|
} from "./hyperlink/helpers";
|
||||||
|
|
||||||
const AppContext = React.createContext<AppClassProperties>(null!);
|
const AppContext = React.createContext<AppClassProperties>(null!);
|
||||||
const AppPropsContext = React.createContext<AppProps>(null!);
|
const AppPropsContext = React.createContext<AppProps>(null!);
|
||||||
|
@ -9571,7 +9573,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// TEST HOOKS
|
// TEST HOOKS
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
h: {
|
h: {
|
||||||
|
@ -9584,6 +9585,7 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createTestHook = () => {
|
||||||
if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) {
|
if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) {
|
||||||
window.h = window.h || ({} as Window["h"]);
|
window.h = window.h || ({} as Window["h"]);
|
||||||
|
|
||||||
|
@ -9599,5 +9601,7 @@ if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createTestHook();
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "../css/variables.module.scss";
|
@import "../../css/variables.module.scss";
|
||||||
|
|
||||||
.excalidraw-hyperlinkContainer {
|
.excalidraw-hyperlinkContainer {
|
||||||
display: flex;
|
display: flex;
|
|
@ -1,22 +1,20 @@
|
||||||
import { AppState, ExcalidrawProps, Point, UIAppState } from "../types";
|
import { AppState, ExcalidrawProps, Point } from "../../types";
|
||||||
import {
|
import {
|
||||||
getShortcutKey,
|
|
||||||
sceneCoordsToViewportCoords,
|
sceneCoordsToViewportCoords,
|
||||||
viewportCoordsToSceneCoords,
|
viewportCoordsToSceneCoords,
|
||||||
wrapEvent,
|
wrapEvent,
|
||||||
} from "../utils";
|
} from "../../utils";
|
||||||
import { getEmbedLink, embeddableURLValidator } from "./embeddable";
|
import { getEmbedLink, embeddableURLValidator } from "../../element/embeddable";
|
||||||
import { mutateElement } from "./mutateElement";
|
import { mutateElement } from "../../element/mutateElement";
|
||||||
import {
|
import {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawEmbeddableElement,
|
ExcalidrawEmbeddableElement,
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
} from "./types";
|
} from "../../element/types";
|
||||||
|
|
||||||
import { register } from "../actions/register";
|
import { ToolButton } from "../ToolButton";
|
||||||
import { ToolButton } from "../components/ToolButton";
|
import { FreedrawIcon, TrashIcon } from "../icons";
|
||||||
import { FreedrawIcon, LinkIcon, TrashIcon } from "../components/icons";
|
import { t } from "../../i18n";
|
||||||
import { t } from "../i18n";
|
|
||||||
import {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
@ -25,21 +23,19 @@ import {
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../../keys";
|
||||||
import { DEFAULT_LINK_SIZE } from "../renderer/renderElement";
|
import { EVENT, HYPERLINK_TOOLTIP_DELAY } from "../../constants";
|
||||||
import { rotate } from "../math";
|
import { getElementAbsoluteCoords } from "../../element/bounds";
|
||||||
import { EVENT, HYPERLINK_TOOLTIP_DELAY, MIME_TYPES } from "../constants";
|
import { getTooltipDiv, updateTooltipPosition } from "../Tooltip";
|
||||||
import { Bounds } from "./bounds";
|
import { getSelectedElements } from "../../scene";
|
||||||
import { getTooltipDiv, updateTooltipPosition } from "../components/Tooltip";
|
import { isPointHittingElementBoundingBox } from "../../element/collision";
|
||||||
import { getSelectedElements } from "../scene";
|
import { isLocalLink, normalizeLink } from "../../data/url";
|
||||||
import { isPointHittingElementBoundingBox } from "./collision";
|
|
||||||
import { getElementAbsoluteCoords } from ".";
|
|
||||||
import { isLocalLink, normalizeLink } from "../data/url";
|
|
||||||
|
|
||||||
import "./Hyperlink.scss";
|
import "./Hyperlink.scss";
|
||||||
import { trackEvent } from "../analytics";
|
import { trackEvent } from "../../analytics";
|
||||||
import { useAppProps, useExcalidrawAppState } from "../components/App";
|
import { useAppProps, useExcalidrawAppState } from "../App";
|
||||||
import { isEmbeddableElement } from "./typeChecks";
|
import { isEmbeddableElement } from "../../element/typeChecks";
|
||||||
|
import { getLinkHandleFromCoords } from "./helpers";
|
||||||
|
|
||||||
const CONTAINER_WIDTH = 320;
|
const CONTAINER_WIDTH = 320;
|
||||||
const SPACE_BOTTOM = 85;
|
const SPACE_BOTTOM = 85;
|
||||||
|
@ -47,11 +43,6 @@ const CONTAINER_PADDING = 5;
|
||||||
const CONTAINER_HEIGHT = 42;
|
const CONTAINER_HEIGHT = 42;
|
||||||
const AUTO_HIDE_TIMEOUT = 500;
|
const AUTO_HIDE_TIMEOUT = 500;
|
||||||
|
|
||||||
export const EXTERNAL_LINK_IMG = document.createElement("img");
|
|
||||||
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>`,
|
|
||||||
)}`;
|
|
||||||
|
|
||||||
let IS_HYPERLINK_TOOLTIP_VISIBLE = false;
|
let IS_HYPERLINK_TOOLTIP_VISIBLE = false;
|
||||||
|
|
||||||
const embeddableLinkCache = new Map<
|
const embeddableLinkCache = new Map<
|
||||||
|
@ -339,51 +330,6 @@ const getCoordsForPopover = (
|
||||||
return { x, y };
|
return { x, y };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actionLink = register({
|
|
||||||
name: "hyperlink",
|
|
||||||
perform: (elements, appState) => {
|
|
||||||
if (appState.showHyperlinkPopup === "editor") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
elements,
|
|
||||||
appState: {
|
|
||||||
...appState,
|
|
||||||
showHyperlinkPopup: "editor",
|
|
||||||
openMenu: null,
|
|
||||||
},
|
|
||||||
commitToHistory: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
trackEvent: { category: "hyperlink", action: "click" },
|
|
||||||
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.K,
|
|
||||||
contextItemLabel: (elements, appState) =>
|
|
||||||
getContextMenuLabel(elements, appState),
|
|
||||||
predicate: (elements, appState) => {
|
|
||||||
const selectedElements = getSelectedElements(elements, appState);
|
|
||||||
return selectedElements.length === 1;
|
|
||||||
},
|
|
||||||
PanelComponent: ({ elements, appState, updateData }) => {
|
|
||||||
const selectedElements = getSelectedElements(elements, appState);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolButton
|
|
||||||
type="button"
|
|
||||||
icon={LinkIcon}
|
|
||||||
aria-label={t(getContextMenuLabel(elements, appState))}
|
|
||||||
title={`${
|
|
||||||
isEmbeddableElement(elements[0])
|
|
||||||
? t("labels.link.labelEmbed")
|
|
||||||
: t("labels.link.label")
|
|
||||||
} - ${getShortcutKey("CtrlOrCmd+K")}`}
|
|
||||||
onClick={() => updateData(null)}
|
|
||||||
selected={selectedElements.length === 1 && !!selectedElements[0].link}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getContextMenuLabel = (
|
export const getContextMenuLabel = (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
|
@ -399,87 +345,6 @@ export const getContextMenuLabel = (
|
||||||
return label;
|
return label;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLinkHandleFromCoords = (
|
|
||||||
[x1, y1, x2, y2]: Bounds,
|
|
||||||
angle: number,
|
|
||||||
appState: Pick<UIAppState, "zoom">,
|
|
||||||
): Bounds => {
|
|
||||||
const size = DEFAULT_LINK_SIZE;
|
|
||||||
const linkWidth = size / appState.zoom.value;
|
|
||||||
const linkHeight = size / appState.zoom.value;
|
|
||||||
const linkMarginY = size / appState.zoom.value;
|
|
||||||
const centerX = (x1 + x2) / 2;
|
|
||||||
const centerY = (y1 + y2) / 2;
|
|
||||||
const centeringOffset = (size - 8) / (2 * appState.zoom.value);
|
|
||||||
const dashedLineMargin = 4 / appState.zoom.value;
|
|
||||||
|
|
||||||
// Same as `ne` resize handle
|
|
||||||
const x = x2 + dashedLineMargin - centeringOffset;
|
|
||||||
const y = y1 - dashedLineMargin - linkMarginY + centeringOffset;
|
|
||||||
|
|
||||||
const [rotatedX, rotatedY] = rotate(
|
|
||||||
x + linkWidth / 2,
|
|
||||||
y + linkHeight / 2,
|
|
||||||
centerX,
|
|
||||||
centerY,
|
|
||||||
angle,
|
|
||||||
);
|
|
||||||
return [
|
|
||||||
rotatedX - linkWidth / 2,
|
|
||||||
rotatedY - linkHeight / 2,
|
|
||||||
linkWidth,
|
|
||||||
linkHeight,
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isPointHittingLinkIcon = (
|
|
||||||
element: NonDeletedExcalidrawElement,
|
|
||||||
elementsMap: ElementsMap,
|
|
||||||
appState: AppState,
|
|
||||||
[x, y]: Point,
|
|
||||||
) => {
|
|
||||||
const threshold = 4 / appState.zoom.value;
|
|
||||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
||||||
const [linkX, linkY, linkWidth, linkHeight] = getLinkHandleFromCoords(
|
|
||||||
[x1, y1, x2, y2],
|
|
||||||
element.angle,
|
|
||||||
appState,
|
|
||||||
);
|
|
||||||
const hitLink =
|
|
||||||
x > linkX - threshold &&
|
|
||||||
x < linkX + threshold + linkWidth &&
|
|
||||||
y > linkY - threshold &&
|
|
||||||
y < linkY + linkHeight + threshold;
|
|
||||||
return hitLink;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isPointHittingLink = (
|
|
||||||
element: NonDeletedExcalidrawElement,
|
|
||||||
elementsMap: ElementsMap,
|
|
||||||
appState: AppState,
|
|
||||||
[x, y]: Point,
|
|
||||||
isMobile: boolean,
|
|
||||||
) => {
|
|
||||||
if (!element.link || appState.selectedElementIds[element.id]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const threshold = 4 / appState.zoom.value;
|
|
||||||
if (
|
|
||||||
!isMobile &&
|
|
||||||
appState.viewModeEnabled &&
|
|
||||||
isPointHittingElementBoundingBox(
|
|
||||||
element,
|
|
||||||
elementsMap,
|
|
||||||
[x, y],
|
|
||||||
threshold,
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return isPointHittingLinkIcon(element, elementsMap, appState, [x, y]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let HYPERLINK_TOOLTIP_TIMEOUT_ID: number | null = null;
|
let HYPERLINK_TOOLTIP_TIMEOUT_ID: number | null = null;
|
||||||
export const showHyperlinkTooltip = (
|
export const showHyperlinkTooltip = (
|
||||||
element: NonDeletedExcalidrawElement,
|
element: NonDeletedExcalidrawElement,
|
||||||
|
@ -547,7 +412,7 @@ export const hideHyperlinkToolip = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shouldHideLinkPopup = (
|
const shouldHideLinkPopup = (
|
||||||
element: NonDeletedExcalidrawElement,
|
element: NonDeletedExcalidrawElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
appState: AppState,
|
appState: AppState,
|
93
packages/excalidraw/components/hyperlink/helpers.ts
Normal file
93
packages/excalidraw/components/hyperlink/helpers.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import { MIME_TYPES } from "../../constants";
|
||||||
|
import { Bounds, getElementAbsoluteCoords } from "../../element/bounds";
|
||||||
|
import { isPointHittingElementBoundingBox } from "../../element/collision";
|
||||||
|
import { ElementsMap, NonDeletedExcalidrawElement } from "../../element/types";
|
||||||
|
import { rotate } from "../../math";
|
||||||
|
import { DEFAULT_LINK_SIZE } from "../../renderer/renderElement";
|
||||||
|
import { AppState, Point, UIAppState } from "../../types";
|
||||||
|
|
||||||
|
export const EXTERNAL_LINK_IMG = document.createElement("img");
|
||||||
|
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>`,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
export const getLinkHandleFromCoords = (
|
||||||
|
[x1, y1, x2, y2]: Bounds,
|
||||||
|
angle: number,
|
||||||
|
appState: Pick<UIAppState, "zoom">,
|
||||||
|
): Bounds => {
|
||||||
|
const size = DEFAULT_LINK_SIZE;
|
||||||
|
const linkWidth = size / appState.zoom.value;
|
||||||
|
const linkHeight = size / appState.zoom.value;
|
||||||
|
const linkMarginY = size / appState.zoom.value;
|
||||||
|
const centerX = (x1 + x2) / 2;
|
||||||
|
const centerY = (y1 + y2) / 2;
|
||||||
|
const centeringOffset = (size - 8) / (2 * appState.zoom.value);
|
||||||
|
const dashedLineMargin = 4 / appState.zoom.value;
|
||||||
|
|
||||||
|
// Same as `ne` resize handle
|
||||||
|
const x = x2 + dashedLineMargin - centeringOffset;
|
||||||
|
const y = y1 - dashedLineMargin - linkMarginY + centeringOffset;
|
||||||
|
|
||||||
|
const [rotatedX, rotatedY] = rotate(
|
||||||
|
x + linkWidth / 2,
|
||||||
|
y + linkHeight / 2,
|
||||||
|
centerX,
|
||||||
|
centerY,
|
||||||
|
angle,
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
rotatedX - linkWidth / 2,
|
||||||
|
rotatedY - linkHeight / 2,
|
||||||
|
linkWidth,
|
||||||
|
linkHeight,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isPointHittingLinkIcon = (
|
||||||
|
element: NonDeletedExcalidrawElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
appState: AppState,
|
||||||
|
[x, y]: Point,
|
||||||
|
) => {
|
||||||
|
const threshold = 4 / appState.zoom.value;
|
||||||
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
||||||
|
const [linkX, linkY, linkWidth, linkHeight] = getLinkHandleFromCoords(
|
||||||
|
[x1, y1, x2, y2],
|
||||||
|
element.angle,
|
||||||
|
appState,
|
||||||
|
);
|
||||||
|
const hitLink =
|
||||||
|
x > linkX - threshold &&
|
||||||
|
x < linkX + threshold + linkWidth &&
|
||||||
|
y > linkY - threshold &&
|
||||||
|
y < linkY + linkHeight + threshold;
|
||||||
|
return hitLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isPointHittingLink = (
|
||||||
|
element: NonDeletedExcalidrawElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
appState: AppState,
|
||||||
|
[x, y]: Point,
|
||||||
|
isMobile: boolean,
|
||||||
|
) => {
|
||||||
|
if (!element.link || appState.selectedElementIds[element.id]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const threshold = 4 / appState.zoom.value;
|
||||||
|
if (
|
||||||
|
!isMobile &&
|
||||||
|
appState.viewModeEnabled &&
|
||||||
|
isPointHittingElementBoundingBox(
|
||||||
|
element,
|
||||||
|
elementsMap,
|
||||||
|
[x, y],
|
||||||
|
threshold,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isPointHittingLinkIcon(element, elementsMap, appState, [x, y]);
|
||||||
|
};
|
|
@ -73,7 +73,7 @@ import {
|
||||||
import {
|
import {
|
||||||
EXTERNAL_LINK_IMG,
|
EXTERNAL_LINK_IMG,
|
||||||
getLinkHandleFromCoords,
|
getLinkHandleFromCoords,
|
||||||
} from "../element/Hyperlink";
|
} from "../components/hyperlink/helpers";
|
||||||
import { renderSnaps } from "./renderSnaps";
|
import { renderSnaps } from "./renderSnaps";
|
||||||
import {
|
import {
|
||||||
isEmbeddableElement,
|
isEmbeddableElement,
|
||||||
|
|
|
@ -31,8 +31,11 @@ import { getSelectedElements } from "../../scene/selection";
|
||||||
import { isLinearElementType } from "../../element/typeChecks";
|
import { isLinearElementType } from "../../element/typeChecks";
|
||||||
import { Mutable } from "../../utility-types";
|
import { Mutable } from "../../utility-types";
|
||||||
import { assertNever } from "../../utils";
|
import { assertNever } from "../../utils";
|
||||||
|
import { createTestHook } from "../../components/App";
|
||||||
|
|
||||||
const readFile = util.promisify(fs.readFile);
|
const readFile = util.promisify(fs.readFile);
|
||||||
|
// so that window.h is available when App.tsx is not imported as well.
|
||||||
|
createTestHook();
|
||||||
|
|
||||||
const { h } = window;
|
const { h } = window;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,10 @@ import { getCommonBounds, getElementPointsCoords } from "../../element/bounds";
|
||||||
import { rotatePoint } from "../../math";
|
import { rotatePoint } from "../../math";
|
||||||
import { getTextEditor } from "../queries/dom";
|
import { getTextEditor } from "../queries/dom";
|
||||||
import { arrayToMap } from "../../utils";
|
import { arrayToMap } from "../../utils";
|
||||||
|
import { createTestHook } from "../../components/App";
|
||||||
|
|
||||||
|
// so that window.h is available when App.tsx is not imported as well.
|
||||||
|
createTestHook();
|
||||||
|
|
||||||
const { h } = window;
|
const { h } = window;
|
||||||
|
|
||||||
|
@ -460,7 +464,6 @@ export class UI {
|
||||||
mouse.reset();
|
mouse.reset();
|
||||||
mouse.up(x + width, y + height);
|
mouse.up(x + width, y + height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const origElement = h.elements[h.elements.length - 1] as any;
|
const origElement = h.elements[h.elements.length - 1] as any;
|
||||||
|
|
||||||
if (angle !== 0) {
|
if (angle !== 0) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue