1
1
import {
2
2
LinkAttributes ,
3
3
LinkNode as BaseLinkNode ,
4
- SerializedAutoLinkNode ,
5
4
SerializedLinkNode as BaseSerializedLinkNode
6
5
} from "@lexical/link" ;
7
6
import { DOMConversionMap , DOMConversionOutput , EditorConfig , NodeKey , Spread } from "lexical" ;
8
7
import { addClassNamesToElement , isHTMLAnchorElement } from "@lexical/utils" ;
9
8
import { sanitizeUrl } from "~/utils/sanitizeUrl" ;
10
9
10
+ export interface LinkNodeAttributes extends LinkAttributes {
11
+ alt ?: string ;
12
+ }
13
+
11
14
export type SerializedLinkNode = Spread <
12
15
{
13
- type : "link-node" ;
16
+ alt ?: string ;
17
+ type : "link" ;
14
18
version : 1 ;
15
19
} ,
16
- Spread < LinkAttributes , BaseSerializedLinkNode >
20
+ Spread < LinkNodeAttributes , BaseSerializedLinkNode >
17
21
> ;
18
22
23
+ /**
24
+ * NOTES: This class is extended to support custom URLs patterns.
25
+ * - We use custom 'sanitizeUrl' method to control what kind of ULRs we will support or prevent to be added.
26
+ */
19
27
export class LinkNode extends BaseLinkNode {
20
- constructor ( url : string , attributes : LinkAttributes = { } , key ?: NodeKey ) {
21
- super ( url , attributes , key ) ;
22
- }
28
+ __alt ?: string ;
23
29
24
- static override getType ( ) : string {
25
- return "link-node" ;
30
+ constructor ( url : string , attributes : LinkNodeAttributes , key ?: NodeKey ) {
31
+ super ( url , attributes , key ) ;
32
+ this . __alt = attributes . alt ;
26
33
}
27
34
28
35
static override clone ( node : LinkNode ) : LinkNode {
29
36
return new LinkNode (
30
37
node . __url ,
31
- { rel : node . __rel , target : node . __target , title : node . __title } ,
38
+ { rel : node . __rel , target : node . __target , title : node . __title , alt : node . __alt } ,
32
39
node . __key
33
40
) ;
34
41
}
35
42
43
+ getAlt ( ) : string | undefined {
44
+ return this . __alt ;
45
+ }
46
+
47
+ setAlt ( text : string ) : this {
48
+ const self = super . getWritable ( ) ;
49
+ self . __alt = text ;
50
+ return self ;
51
+ }
52
+
36
53
override createDOM ( config : EditorConfig ) : HTMLAnchorElement {
37
54
const element = document . createElement ( "a" ) ;
55
+ /**
56
+ * Use custom sanitization function for the URL.
57
+ */
38
58
element . href = sanitizeUrl ( this . __url ) ;
39
59
if ( this . __target !== null ) {
40
60
element . target = this . __target ;
@@ -45,10 +65,23 @@ export class LinkNode extends BaseLinkNode {
45
65
if ( this . __title !== null ) {
46
66
element . title = this . __title ;
47
67
}
68
+ if ( this . __alt ) {
69
+ element . setAttribute ( "alt" , this . __alt ) ;
70
+ }
71
+
48
72
addClassNamesToElement ( element , config . theme . link ) ;
49
73
return element ;
50
74
}
51
75
76
+ override updateDOM ( prevNode : LinkNode , dom : HTMLElement ) : boolean {
77
+ if ( this . __alt ) {
78
+ dom . setAttribute ( "alt" , this . __alt ) ;
79
+ }
80
+ // Returning false tells Lexical that this node does not need its
81
+ // DOM element replacing with a new copy from createDOM.
82
+ return false ;
83
+ }
84
+
52
85
static override importDOM ( ) : DOMConversionMap | null {
53
86
return {
54
87
a : ( ) => ( {
@@ -58,48 +91,53 @@ export class LinkNode extends BaseLinkNode {
58
91
} ;
59
92
}
60
93
61
- static override importJSON (
62
- serializedNode : BaseSerializedLinkNode | SerializedLinkNode | SerializedAutoLinkNode
63
- ) : LinkNode {
94
+ static override importJSON ( serializedNode : SerializedLinkNode ) : LinkNode {
64
95
const node = $createLinkNode ( serializedNode . url , {
65
96
rel : serializedNode . rel ,
66
97
target : serializedNode . target ,
67
- title : serializedNode . title
98
+ title : serializedNode . title ,
99
+ alt : serializedNode . alt
68
100
} ) ;
69
101
node . setFormat ( serializedNode . format ) ;
70
102
node . setIndent ( serializedNode . indent ) ;
71
103
node . setDirection ( serializedNode . direction ) ;
104
+
105
+ if ( serializedNode . alt ) {
106
+ node . setAlt ( serializedNode . alt ) ;
107
+ }
72
108
return node ;
73
109
}
74
110
75
- override exportJSON ( ) : BaseSerializedLinkNode | SerializedLinkNode | SerializedAutoLinkNode {
111
+ override exportJSON ( ) : SerializedLinkNode {
76
112
return {
77
113
...super . exportJSON ( ) ,
78
- type : "link-node" ,
114
+ alt : this . __alt ,
115
+ type : "link" ,
79
116
version : 1
80
117
} ;
81
118
}
82
119
}
83
120
84
- function convertAnchorElement ( domNode : Node ) : DOMConversionOutput {
121
+ const convertAnchorElement = ( domNode : Node ) : DOMConversionOutput => {
85
122
let node = null ;
86
123
if ( isHTMLAnchorElement ( domNode ) ) {
87
124
const content = domNode . textContent ;
88
125
if ( content !== null && content !== "" ) {
89
126
node = $createLinkNode ( domNode . getAttribute ( "href" ) || "" , {
90
127
rel : domNode . getAttribute ( "rel" ) ,
91
128
target : domNode . getAttribute ( "target" ) ,
92
- title : domNode . getAttribute ( "title" )
129
+ title : domNode . getAttribute ( "title" ) ,
130
+ alt : domNode . getAttribute ( "alt" ) ?? undefined
93
131
} ) ;
94
132
}
95
133
}
96
134
return { node } ;
97
- }
135
+ } ;
98
136
99
137
export const $isLinkNode = ( node : any ) : node is LinkNode => {
100
138
return node instanceof LinkNode ;
101
139
} ;
102
140
103
- export const $createLinkNode = ( url : string , attributes : LinkAttributes = { } , key ?: KeyType ) => {
141
+ export const $createLinkNode = ( url : string , attributes : LinkNodeAttributes , key ?: KeyType ) => {
104
142
return new LinkNode ( url , attributes , key ) ;
105
143
} ;
0 commit comments