1111 * only way a later value arrives.
1212 */
1313
14+ import { useEffect } from "react" ;
15+ import { $getSelection , $isRangeSelection } from "lexical" ;
16+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext" ;
1417import { addComposerChild$ , realmPlugin } from "@mdxeditor/editor" ;
15- import { ClickableLinkPlugin } from "@lexical/react/LexicalClickableLinkPlugin" ;
1618
1719import { ChangeObserver } from "./changes-observer" ;
1820import { CommentLayer } from "./comment-layer" ;
@@ -34,6 +36,42 @@ import type { WidgetOptions } from "./widget-options";
3436export { widgets$ } from "./widget-options" ;
3537export type { WidgetOptions } from "./widget-options" ;
3638
39+ function SafeClickableLinkPlugin ( ) {
40+ let [ editor ] = useLexicalComposerContext ( ) ;
41+
42+ useEffect ( ( ) => {
43+ let follow = ( event : MouseEvent ) => {
44+ if ( event . defaultPrevented || ( event . button !== 0 && event . button !== 1 ) ) return ;
45+ let target = event . target ;
46+ if ( ! ( target instanceof Element ) ) return ;
47+ let link = target . closest < HTMLAnchorElement > ( "a[href]" ) ;
48+ if ( ! link || link . hasAttribute ( "download" ) ) return ;
49+ let selecting = editor . read ( ( ) => {
50+ let selection = $getSelection ( ) ;
51+ return $isRangeSelection ( selection ) && ! selection . isCollapsed ( ) ;
52+ } ) ;
53+ if ( selecting ) {
54+ event . preventDefault ( ) ;
55+ return ;
56+ }
57+ event . preventDefault ( ) ;
58+ event . stopPropagation ( ) ;
59+ window . open ( link . href , "_blank" , "noopener,noreferrer" ) ;
60+ } ;
61+ let middle = ( event : MouseEvent ) => {
62+ if ( event . button === 1 ) follow ( event ) ;
63+ } ;
64+ return editor . registerRootListener ( ( current , previous ) => {
65+ previous ?. removeEventListener ( "click" , follow ) ;
66+ previous ?. removeEventListener ( "mouseup" , middle ) ;
67+ current ?. addEventListener ( "click" , follow ) ;
68+ current ?. addEventListener ( "mouseup" , middle ) ;
69+ } ) ;
70+ } , [ editor ] ) ;
71+
72+ return null ;
73+ }
74+
3775export const widgetsPlugin = realmPlugin < WidgetOptions > ( {
3876 init ( realm , params ) {
3977 realm . pub ( widgets$ , params ?? { } ) ;
@@ -59,7 +97,7 @@ export const widgetsPlugin = realmPlugin<WidgetOptions>({
5997 realm . pub ( addComposerChild$ , ResearchDeletionPlugin ) ;
6098 // Link nodes live inside Lexical's contenteditable root, where a normal
6199 // browser click changes the selection instead of following the anchor.
62- realm . pub ( addComposerChild$ , ( ) => < ClickableLinkPlugin newTab /> ) ;
100+ realm . pub ( addComposerChild$ , SafeClickableLinkPlugin ) ;
63101 // Also where `@lexical/table`'s own plugins are registered, which the
64102 // editor otherwise runs without.
65103 realm . pub ( addComposerChild$ , TableChrome ) ;
0 commit comments