@docs.plus/extension-hyperlink 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/dist/helpers/autoHyperlink.d.ts +8 -0
- package/dist/helpers/clickHandler.d.ts +16 -0
- package/dist/helpers/editHyperlink.d.ts +9 -0
- package/dist/helpers/pasteHandler.d.ts +9 -0
- package/dist/helpers/tippyHelper.d.ts +20 -0
- package/dist/hyperlink.d.ts +75 -0
- package/dist/index.cjs +496 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +490 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +497 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +44 -0
- package/src/helpers/autoHyperlink.ts +167 -0
- package/src/helpers/clickHandler.ts +76 -0
- package/src/helpers/editHyperlink.ts +55 -0
- package/src/helpers/pasteHandler.ts +46 -0
- package/src/helpers/tippyHelper.ts +126 -0
- package/src/hyperlink.ts +272 -0
- package/src/index.ts +6 -0
package/src/hyperlink.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { Mark, markPasteRule, mergeAttributes } from "@tiptap/core";
|
|
2
|
+
import { Plugin } from "@tiptap/pm/state";
|
|
3
|
+
import { find, registerCustomProtocol, reset } from "linkifyjs";
|
|
4
|
+
import autoHyperlink from "./helpers/autoHyperlink";
|
|
5
|
+
import clickHandler from "./helpers/clickHandler";
|
|
6
|
+
import { pasteHandler } from "./helpers/pasteHandler";
|
|
7
|
+
import editHyperlinkHelper from "./helpers/editHyperlink";
|
|
8
|
+
|
|
9
|
+
export interface LinkProtocolOptions {
|
|
10
|
+
scheme: string;
|
|
11
|
+
optionalSlashes?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface HyperlinkOptions {
|
|
15
|
+
/**
|
|
16
|
+
* If enabled, it adds links as you type.
|
|
17
|
+
*/
|
|
18
|
+
autoHyperlink: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* An array of custom protocols to be registered with linkifyjs.
|
|
21
|
+
*/
|
|
22
|
+
protocols: Array<LinkProtocolOptions | string>;
|
|
23
|
+
/**
|
|
24
|
+
* If enabled, links will be opened on click.
|
|
25
|
+
*/
|
|
26
|
+
openOnClick: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Adds a link to the current selection if the pasted content only contains an url.
|
|
29
|
+
*/
|
|
30
|
+
hyperlinkOnPaste: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* A list of HTML attributes to be rendered.
|
|
33
|
+
*/
|
|
34
|
+
HTMLAttributes: Record<string, any>;
|
|
35
|
+
/**
|
|
36
|
+
* A list of modals to be rendered.
|
|
37
|
+
* @default null
|
|
38
|
+
* @example
|
|
39
|
+
*/
|
|
40
|
+
modals: {
|
|
41
|
+
previewHyperlink?: ((options: any) => HTMLElement) | null;
|
|
42
|
+
setHyperlink?: ((options: any) => HTMLElement) | null;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* A validation function that modifies link verification for the auto linker.
|
|
46
|
+
* @param url - The url to be validated.
|
|
47
|
+
* @returns - True if the url is valid, false otherwise.
|
|
48
|
+
*/
|
|
49
|
+
validate?: (url: string) => boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare module "@tiptap/core" {
|
|
53
|
+
interface Commands<ReturnType> {
|
|
54
|
+
link: {
|
|
55
|
+
/**
|
|
56
|
+
* Edit the hyperlink's text
|
|
57
|
+
*/
|
|
58
|
+
editHyperLinkText: (text: string) => ReturnType;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Edit the hyperlink's href value
|
|
62
|
+
*/
|
|
63
|
+
editHyperLinkHref: (href: string) => ReturnType;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Edit the hyperlink's
|
|
67
|
+
*/
|
|
68
|
+
editHyperlink: (attributes?: { newText?: string; newURL?: string }) => ReturnType;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set a hyperlink
|
|
72
|
+
*/
|
|
73
|
+
setHyperlink: (attributes?: { href: string; target?: string | null }) => ReturnType;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Unset a link mark
|
|
77
|
+
*/
|
|
78
|
+
unsetHyperlink: () => ReturnType;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const Hyperlink = Mark.create<HyperlinkOptions>({
|
|
84
|
+
name: "hyperlink",
|
|
85
|
+
|
|
86
|
+
priority: 1000,
|
|
87
|
+
|
|
88
|
+
keepOnSplit: false,
|
|
89
|
+
|
|
90
|
+
onCreate() {
|
|
91
|
+
this.options.protocols.forEach((protocol) => {
|
|
92
|
+
if (typeof protocol === "string") {
|
|
93
|
+
registerCustomProtocol(protocol);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
registerCustomProtocol(protocol.scheme, protocol.optionalSlashes);
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
onDestroy() {
|
|
101
|
+
reset();
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
inclusive() {
|
|
105
|
+
return this.options.autoHyperlink;
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
addOptions() {
|
|
109
|
+
return {
|
|
110
|
+
openOnClick: true,
|
|
111
|
+
hyperlinkOnPaste: true,
|
|
112
|
+
autoHyperlink: true,
|
|
113
|
+
protocols: [],
|
|
114
|
+
HTMLAttributes: {
|
|
115
|
+
target: "_blank",
|
|
116
|
+
rel: "noopener noreferrer nofollow",
|
|
117
|
+
class: null,
|
|
118
|
+
},
|
|
119
|
+
modals: {
|
|
120
|
+
previewHyperlink: null,
|
|
121
|
+
setHyperlink: null,
|
|
122
|
+
},
|
|
123
|
+
validate: undefined,
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
addAttributes() {
|
|
128
|
+
return {
|
|
129
|
+
href: {
|
|
130
|
+
default: null,
|
|
131
|
+
},
|
|
132
|
+
target: {
|
|
133
|
+
default: this.options.HTMLAttributes.target,
|
|
134
|
+
},
|
|
135
|
+
class: {
|
|
136
|
+
default: this.options.HTMLAttributes.class,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
parseHTML() {
|
|
142
|
+
return [{ tag: 'a[href]:not([href *= "javascript:" i])' }];
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
renderHTML({ HTMLAttributes }) {
|
|
146
|
+
return ["a", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
addCommands() {
|
|
150
|
+
return {
|
|
151
|
+
setHyperlink:
|
|
152
|
+
(attributes) =>
|
|
153
|
+
({ editor, chain }) => {
|
|
154
|
+
if (!this.options.modals.setHyperlink) {
|
|
155
|
+
return chain()
|
|
156
|
+
.setMark(this.name, attributes)
|
|
157
|
+
.setMeta("preventAutohyperlink", true)
|
|
158
|
+
.run();
|
|
159
|
+
} else {
|
|
160
|
+
this.options.modals.setHyperlink({
|
|
161
|
+
editor,
|
|
162
|
+
validate: this.options.validate,
|
|
163
|
+
extentionName: this.name,
|
|
164
|
+
attributes,
|
|
165
|
+
});
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
editHyperlink:
|
|
171
|
+
(attributes) =>
|
|
172
|
+
({ editor }) => {
|
|
173
|
+
return editHyperlinkHelper({
|
|
174
|
+
...attributes,
|
|
175
|
+
editor,
|
|
176
|
+
validate: this.options.validate,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
editHyperLinkText:
|
|
181
|
+
(newText) =>
|
|
182
|
+
({ editor }) => {
|
|
183
|
+
return editHyperlinkHelper({
|
|
184
|
+
editor,
|
|
185
|
+
newText,
|
|
186
|
+
validate: this.options.validate,
|
|
187
|
+
});
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
editHyperLinkHref:
|
|
191
|
+
(newURL) =>
|
|
192
|
+
({ editor }) => {
|
|
193
|
+
return editHyperlinkHelper({
|
|
194
|
+
editor,
|
|
195
|
+
validate: this.options.validate,
|
|
196
|
+
newURL,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
unsetHyperlink:
|
|
201
|
+
() =>
|
|
202
|
+
({ chain }) => {
|
|
203
|
+
return chain()
|
|
204
|
+
.unsetMark(this.name, { extendEmptyMarkRange: true })
|
|
205
|
+
.setMeta("preventAutohyperlink", true)
|
|
206
|
+
.run();
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
addPasteRules() {
|
|
212
|
+
return [
|
|
213
|
+
markPasteRule({
|
|
214
|
+
find: (text) =>
|
|
215
|
+
find(text)
|
|
216
|
+
.filter((link) => {
|
|
217
|
+
if (this.options.validate) {
|
|
218
|
+
return this.options.validate(link.value);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true;
|
|
222
|
+
})
|
|
223
|
+
.filter((link) => link.isLink)
|
|
224
|
+
.map((link) => ({
|
|
225
|
+
text: link.value,
|
|
226
|
+
index: link.start,
|
|
227
|
+
data: link,
|
|
228
|
+
})),
|
|
229
|
+
type: this.type,
|
|
230
|
+
getAttributes: (match) => ({
|
|
231
|
+
href: match.data?.href,
|
|
232
|
+
}),
|
|
233
|
+
}),
|
|
234
|
+
];
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
addProseMirrorPlugins() {
|
|
238
|
+
const plugins: Plugin[] = [];
|
|
239
|
+
|
|
240
|
+
if (this.options.autoHyperlink) {
|
|
241
|
+
plugins.push(
|
|
242
|
+
autoHyperlink({
|
|
243
|
+
type: this.type,
|
|
244
|
+
validate: this.options.validate,
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (this.options.openOnClick) {
|
|
250
|
+
plugins.push(
|
|
251
|
+
clickHandler({
|
|
252
|
+
type: this.type,
|
|
253
|
+
editor: this.editor,
|
|
254
|
+
validate: this.options.validate,
|
|
255
|
+
view: this.editor.view,
|
|
256
|
+
modals: this.options.modals,
|
|
257
|
+
})
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (this.options.hyperlinkOnPaste) {
|
|
262
|
+
plugins.push(
|
|
263
|
+
pasteHandler({
|
|
264
|
+
editor: this.editor,
|
|
265
|
+
type: this.type,
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return plugins;
|
|
271
|
+
},
|
|
272
|
+
});
|