fix: Links with strikethrough do not have hover preview (#4841)
* fix: Links with strikethrough do not have hover preview * refactor
This commit is contained in:
@@ -65,19 +65,18 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
const localRef = React.useRef<SharedEditor>();
|
||||
const preferences = auth.user?.preferences;
|
||||
const previousHeadings = React.useRef<Heading[] | null>(null);
|
||||
|
||||
const [
|
||||
activeLinkEvent,
|
||||
setActiveLinkEvent,
|
||||
] = React.useState<MouseEvent | null>(null);
|
||||
activeLinkElement,
|
||||
setActiveLink,
|
||||
] = React.useState<HTMLAnchorElement | null>(null);
|
||||
|
||||
const handleLinkActive = React.useCallback((event: MouseEvent) => {
|
||||
setActiveLinkEvent(event);
|
||||
const handleLinkActive = React.useCallback((element: HTMLAnchorElement) => {
|
||||
setActiveLink(element);
|
||||
return false;
|
||||
}, []);
|
||||
|
||||
const handleLinkInactive = React.useCallback(() => {
|
||||
setActiveLinkEvent(null);
|
||||
setActiveLink(null);
|
||||
}, []);
|
||||
|
||||
const handleSearchLink = React.useCallback(
|
||||
@@ -309,10 +308,9 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
minHeight={props.bottomPadding}
|
||||
/>
|
||||
)}
|
||||
{activeLinkEvent && !shareId && (
|
||||
{activeLinkElement && !shareId && (
|
||||
<HoverPreview
|
||||
node={activeLinkEvent.target as HTMLAnchorElement}
|
||||
event={activeLinkEvent}
|
||||
element={activeLinkElement}
|
||||
onClose={handleLinkInactive}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -14,14 +14,13 @@ const DELAY_OPEN = 300;
|
||||
const DELAY_CLOSE = 300;
|
||||
|
||||
type Props = {
|
||||
node: HTMLAnchorElement;
|
||||
event: MouseEvent;
|
||||
element: HTMLAnchorElement;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
function HoverPreviewInternal({ node, onClose }: Props) {
|
||||
function HoverPreviewInternal({ element, onClose }: Props) {
|
||||
const { documents } = useStores();
|
||||
const slug = parseDocumentSlug(node.href);
|
||||
const slug = parseDocumentSlug(element.href);
|
||||
const [isVisible, setVisible] = React.useState(false);
|
||||
const timerClose = React.useRef<ReturnType<typeof setTimeout>>();
|
||||
const timerOpen = React.useRef<ReturnType<typeof setTimeout>>();
|
||||
@@ -68,13 +67,13 @@ function HoverPreviewInternal({ node, onClose }: Props) {
|
||||
cardRef.current.addEventListener("mouseleave", startCloseTimer);
|
||||
}
|
||||
|
||||
node.addEventListener("mouseout", startCloseTimer);
|
||||
node.addEventListener("mouseover", stopCloseTimer);
|
||||
node.addEventListener("mouseover", startOpenTimer);
|
||||
element.addEventListener("mouseout", startCloseTimer);
|
||||
element.addEventListener("mouseover", stopCloseTimer);
|
||||
element.addEventListener("mouseover", startOpenTimer);
|
||||
return () => {
|
||||
node.removeEventListener("mouseout", startCloseTimer);
|
||||
node.removeEventListener("mouseover", stopCloseTimer);
|
||||
node.removeEventListener("mouseover", startOpenTimer);
|
||||
element.removeEventListener("mouseout", startCloseTimer);
|
||||
element.removeEventListener("mouseover", stopCloseTimer);
|
||||
element.removeEventListener("mouseover", startOpenTimer);
|
||||
|
||||
if (cardRef.current) {
|
||||
cardRef.current.removeEventListener("mouseenter", stopCloseTimer);
|
||||
@@ -88,9 +87,9 @@ function HoverPreviewInternal({ node, onClose }: Props) {
|
||||
clearTimeout(timerClose.current);
|
||||
}
|
||||
};
|
||||
}, [node, slug]);
|
||||
}, [element, slug]);
|
||||
|
||||
const anchorBounds = node.getBoundingClientRect();
|
||||
const anchorBounds = element.getBoundingClientRect();
|
||||
const cardBounds = cardRef.current?.getBoundingClientRect();
|
||||
const left = cardBounds
|
||||
? Math.min(anchorBounds.left, window.innerWidth - 16 - 350)
|
||||
@@ -105,7 +104,7 @@ function HoverPreviewInternal({ node, onClose }: Props) {
|
||||
aria-hidden
|
||||
>
|
||||
<div ref={cardRef}>
|
||||
<HoverPreviewDocument url={node.href}>
|
||||
<HoverPreviewDocument url={element.href}>
|
||||
{(content: React.ReactNode) =>
|
||||
isVisible ? (
|
||||
<Animate>
|
||||
@@ -124,18 +123,18 @@ function HoverPreviewInternal({ node, onClose }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
function HoverPreview({ node, ...rest }: Props) {
|
||||
function HoverPreview({ element, ...rest }: Props) {
|
||||
const isMobile = useMobile();
|
||||
if (isMobile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// previews only work for internal doc links for now
|
||||
if (isExternalUrl(node.href)) {
|
||||
if (isExternalUrl(element.href)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <HoverPreviewInternal {...rest} node={node} />;
|
||||
return <HoverPreviewInternal {...rest} element={element} />;
|
||||
}
|
||||
|
||||
const Animate = styled.div`
|
||||
|
||||
@@ -100,7 +100,7 @@ export type Props = {
|
||||
event: MouseEvent | React.MouseEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
/** Callback when user hovers on any link in the document */
|
||||
onHoverLink?: (event: MouseEvent) => boolean;
|
||||
onHoverLink?: (element: HTMLAnchorElement) => boolean;
|
||||
/** Callback when user presses any key with document focused */
|
||||
onKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
/** Collection of embed types to render in the document */
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Mark as ProsemirrorMark,
|
||||
} from "prosemirror-model";
|
||||
import { EditorState, Plugin } from "prosemirror-state";
|
||||
import { Decoration, DecorationSet } from "prosemirror-view";
|
||||
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
||||
import * as React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { isExternalUrl, sanitizeUrl } from "../../utils/urls";
|
||||
@@ -210,29 +210,28 @@ export default class Link extends Mark {
|
||||
},
|
||||
},
|
||||
props: {
|
||||
decorations: (state) => plugin.getState(state),
|
||||
decorations: (state: EditorState) => plugin.getState(state),
|
||||
handleDOMEvents: {
|
||||
mouseover: (view, event: MouseEvent) => {
|
||||
mouseover: (view: EditorView, event: MouseEvent) => {
|
||||
const target = (event.target as HTMLElement)?.closest("a");
|
||||
if (
|
||||
event.target instanceof HTMLAnchorElement &&
|
||||
!event.target.className.includes("ProseMirror-widget") &&
|
||||
target instanceof HTMLAnchorElement &&
|
||||
!target.className.includes("ProseMirror-widget") &&
|
||||
(!view.editable || (view.editable && !view.hasFocus()))
|
||||
) {
|
||||
if (this.options.onHoverLink) {
|
||||
return this.options.onHoverLink(event);
|
||||
return this.options.onHoverLink(target);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
mousedown: (view, event: MouseEvent) => {
|
||||
if (
|
||||
!(event.target instanceof HTMLAnchorElement) ||
|
||||
event.button !== 0
|
||||
) {
|
||||
mousedown: (view: EditorView, event: MouseEvent) => {
|
||||
const target = (event.target as HTMLElement)?.closest("a");
|
||||
if (!(target instanceof HTMLAnchorElement) || event.button !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.target.matches(".component-attachment *")) {
|
||||
if (target.matches(".component-attachment *")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -240,9 +239,9 @@ export default class Link extends Mark {
|
||||
// clicking in read-only will navigate
|
||||
if (!view.editable || (view.editable && !view.hasFocus())) {
|
||||
const href =
|
||||
event.target.href ||
|
||||
(event.target.parentNode instanceof HTMLAnchorElement
|
||||
? event.target.parentNode.href
|
||||
target.href ||
|
||||
(target.parentNode instanceof HTMLAnchorElement
|
||||
? target.parentNode.href
|
||||
: "");
|
||||
|
||||
try {
|
||||
@@ -262,7 +261,7 @@ export default class Link extends Mark {
|
||||
|
||||
return false;
|
||||
},
|
||||
click: (view, event) => {
|
||||
click: (view: EditorView, event: MouseEvent) => {
|
||||
if (
|
||||
!(event.target instanceof HTMLAnchorElement) ||
|
||||
event.button !== 0
|
||||
|
||||
Reference in New Issue
Block a user