11/* eslint-disable @typescript-eslint/no-unsafe-call */
22/* eslint-disable @typescript-eslint/no-unsafe-member-access */
3- import { useEffect , useState } from 'react' ;
3+ import { useEffect , useState , useRef } from 'react' ;
44
55import { Button , IconComponent , Input , Label , Switch } from '@/components' ;
66import Link from '@/extensions/Link/Link' ;
77import { useLocale } from '@/locales' ;
8+ import type { Mark } from '@tiptap/pm/model' ;
89
910interface IPropsLinkEditBlock {
1011 editor : any ;
1112 onSetLink : ( link : string , text ?: string , openInNewTab ?: boolean ) => void ;
13+ open ?: boolean ;
14+ target ?: string ;
1215}
1316
1417function LinkEditBlock ( props : IPropsLinkEditBlock ) {
@@ -20,19 +23,53 @@ function LinkEditBlock(props: IPropsLinkEditBlock) {
2023 } ) ;
2124 const [ openInNewTab , setOpenInNewTab ] = useState < boolean > ( false ) ;
2225
26+ const textInputRef = useRef < HTMLInputElement > ( null ) ;
27+ const linkInputRef = useRef < HTMLInputElement > ( null ) ;
28+
2329 useEffect ( ( ) => {
2430 const updateForm = ( ) => {
25- if ( props . editor ?. isActive ( 'link' ) ) {
26- const { href : link , target } = props . editor . getAttributes ( 'link' ) ;
27- const { from, to } = props . editor . state . selection ;
28- const text = props . editor . state . doc . textBetween ( from , to , ' ' ) ;
29- setForm ( { link : link || '' , text } ) ;
30- setOpenInNewTab ( target === '_blank' ) ;
31- } else {
32- const LinkOptions = props . editor . extensionManager . extensions . find (
33- ( ext : any ) => ext . name === Link . name ,
34- ) ?. options ;
35- setOpenInNewTab ( LinkOptions ?. HTMLAttributes ?. target === '_blank' ) ;
31+ const { from, to, empty } = props . editor . state . selection ;
32+
33+ const LinkOptions = props . editor . extensionManager . extensions . find (
34+ ( ext : any ) => ext . name === Link . name ,
35+ ) ?. options ;
36+
37+ let text = '' ;
38+ let link = '' ;
39+ let target = LinkOptions ?. HTMLAttributes ?. target ;
40+
41+ const node = props . editor . state . doc . nodeAt ( from ) ;
42+
43+ if ( node ) {
44+ const linkMark = node . marks . find ( ( mark : Mark ) => mark . type . name === 'link' ) ;
45+ if ( linkMark ) {
46+ link = linkMark . attrs . href || '' ;
47+ target = linkMark . attrs . target ;
48+ if ( empty ) {
49+ text = node . text || '' ;
50+ } else {
51+ text = props . editor . state . doc . textBetween ( from , to , ' ' ) ;
52+ }
53+ } else {
54+ // no link mark at cursor => normal selection
55+ text = props . editor . state . doc . textBetween ( from , to , ' ' ) ;
56+ }
57+ }
58+ // if no node found (empty doc or weird selection), fallback to range
59+ if ( ! node ) {
60+ text = props . editor . state . doc . textBetween ( from , to , ' ' ) ;
61+ }
62+
63+ setForm ( { link, text } ) ;
64+ setOpenInNewTab ( props . target ? props . target === '_blank' : target === '_blank' ) ;
65+
66+ if ( props . open ) {
67+ // better uexp - focus link input by default
68+ if ( text === '' ) {
69+ textInputRef . current ?. focus ( ) ;
70+ } else {
71+ linkInputRef . current ?. focus ( ) ;
72+ }
3673 }
3774 } ;
3875
@@ -46,7 +83,7 @@ function LinkEditBlock(props: IPropsLinkEditBlock) {
4683 return ( ) => {
4784 props . editor . off ( 'selectionUpdate' , updateForm ) ;
4885 } ;
49- } , [ props . editor ] ) ;
86+ } , [ props . editor , props . open ] ) ;
5087
5188 function handleSubmit ( event : any ) {
5289 event . preventDefault ( ) ;
@@ -65,6 +102,7 @@ function LinkEditBlock(props: IPropsLinkEditBlock) {
65102 < div className = "richtext-mb-[10px] richtext-flex richtext-w-full richtext-max-w-sm richtext-items-center richtext-gap-1.5" >
66103 < div className = "richtext-relative richtext-w-full richtext-max-w-sm richtext-items-center" >
67104 < Input
105+ ref = { textInputRef }
68106 className = "richtext-w-80"
69107 onChange = { ( e ) => setForm ( { ...form , text : e . target . value } ) }
70108 placeholder = "Text"
@@ -82,6 +120,7 @@ function LinkEditBlock(props: IPropsLinkEditBlock) {
82120 < div className = "richtext-flex richtext-w-full richtext-max-w-sm richtext-items-center richtext-gap-1.5" >
83121 < div className = "richtext-relative richtext-w-full richtext-max-w-sm richtext-items-center" >
84122 < Input
123+ ref = { linkInputRef }
85124 className = "richtext-pl-10"
86125 onChange = { ( e ) => setForm ( { ...form , link : e . target . value } ) }
87126 required
@@ -98,18 +137,20 @@ function LinkEditBlock(props: IPropsLinkEditBlock) {
98137 </ div >
99138 </ div >
100139
101- < div className = "richtext-flex richtext-items-center richtext-space-x-2" >
102- < Label >
103- { t ( 'editor.link.dialog.openInNewTab' ) }
104- </ Label >
105-
106- < Switch
107- checked = { openInNewTab }
108- onCheckedChange = { ( e ) => {
109- setOpenInNewTab ( e ) ;
110- } }
111- />
112- </ div >
140+ { ! props . target && (
141+ < div className = "richtext-flex richtext-items-center richtext-space-x-2" >
142+ < Label >
143+ { t ( 'editor.link.dialog.openInNewTab' ) }
144+ </ Label >
145+
146+ < Switch
147+ checked = { openInNewTab }
148+ onCheckedChange = { ( e ) => {
149+ setOpenInNewTab ( e ) ;
150+ } }
151+ />
152+ </ div >
153+ ) }
113154
114155 < Button
115156 className = "richtext-mt-2 richtext-self-end"
0 commit comments