@docs.plus/extension-hyperlink 1.5.1 → 4.3.0
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/CHANGELOG.md +362 -0
- package/LICENSE +21 -0
- package/README.md +263 -365
- package/dist/index.cjs +22 -712
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +255 -0
- package/dist/index.d.ts +255 -9
- package/dist/index.js +22 -707
- package/dist/index.js.map +1 -1
- package/dist/styles.css +323 -0
- package/package.json +78 -31
- package/dist/helpers/autoHyperlink.d.ts +0 -8
- package/dist/helpers/clickHandler.d.ts +0 -11
- package/dist/helpers/editHyperlink.d.ts +0 -9
- package/dist/helpers/pasteHandler.d.ts +0 -9
- package/dist/helpers/tippyHelper.d.ts +0 -29
- package/dist/hyperlink.d.ts +0 -77
- package/dist/index.umd.js +0 -713
- package/dist/index.umd.js.map +0 -1
- package/dist/modals/editeHyperlink.d.ts +0 -11
- package/dist/modals/icons.d.ts +0 -9
- package/dist/modals/previewHyperlink.d.ts +0 -11
- package/dist/modals/setHyperlink.d.ts +0 -14
- package/src/helpers/autoHyperlink.ts +0 -167
- package/src/helpers/clickHandler.ts +0 -80
- package/src/helpers/editHyperlink.ts +0 -59
- package/src/helpers/pasteHandler.ts +0 -46
- package/src/helpers/tippyHelper.ts +0 -136
- package/src/hyperlink.ts +0 -277
- package/src/index.ts +0 -17
- package/src/modals/editeHyperlink.ts +0 -99
- package/src/modals/icons.ts +0 -46
- package/src/modals/previewHyperlink.ts +0 -85
- package/src/modals/setHyperlink.ts +0 -97
package/dist/index.umd.js
DELETED
|
@@ -1,713 +0,0 @@
|
|
|
1
|
-
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/core'), require('linkifyjs'), require('@tiptap/pm/state'), require('tippy.js')) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', '@tiptap/core', 'linkifyjs', '@tiptap/pm/state', 'tippy.js'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global["@docs"] = global["@docs"] || {}, global["@docs"]["plus/extension-hyperlink"] = {}), global.core, global.linkifyjs, global.state, global.tippy));
|
|
5
|
-
})(this, (function (exports, core, linkifyjs, state, tippy) { 'use strict';
|
|
6
|
-
|
|
7
|
-
function autoHyperlink(options) {
|
|
8
|
-
return new state.Plugin({
|
|
9
|
-
key: new state.PluginKey("autoHyperlink"),
|
|
10
|
-
appendTransaction: (transactions, oldState, newState) => {
|
|
11
|
-
const docChanges = transactions.some((transaction) => transaction.docChanged) &&
|
|
12
|
-
!oldState.doc.eq(newState.doc);
|
|
13
|
-
const preventAutoHyperlink = transactions.some((transaction) => transaction.getMeta("preventAutoHyperlink"));
|
|
14
|
-
if (!docChanges || preventAutoHyperlink) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
const { tr } = newState;
|
|
18
|
-
const transform = core.combineTransactionSteps(oldState.doc, [
|
|
19
|
-
...transactions,
|
|
20
|
-
]);
|
|
21
|
-
const { mapping } = transform;
|
|
22
|
-
const changes = core.getChangedRanges(transform);
|
|
23
|
-
changes.forEach(({ oldRange, newRange }) => {
|
|
24
|
-
// at first we check if we have to remove links
|
|
25
|
-
core.getMarksBetween(oldRange.from, oldRange.to, oldState.doc)
|
|
26
|
-
.filter((item) => item.mark.type === options.type)
|
|
27
|
-
.forEach((oldMark) => {
|
|
28
|
-
const newFrom = mapping.map(oldMark.from);
|
|
29
|
-
const newTo = mapping.map(oldMark.to);
|
|
30
|
-
const newMarks = core.getMarksBetween(newFrom, newTo, newState.doc).filter((item) => item.mark.type === options.type);
|
|
31
|
-
if (!newMarks.length) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const newMark = newMarks[0];
|
|
35
|
-
const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to, undefined, " ");
|
|
36
|
-
const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, " ");
|
|
37
|
-
const wasLink = linkifyjs.test(oldLinkText);
|
|
38
|
-
const isLink = linkifyjs.test(newLinkText);
|
|
39
|
-
// remove only the link, if it was a link before too
|
|
40
|
-
// because we don’t want to remove links that were set manually
|
|
41
|
-
if (wasLink && !isLink) {
|
|
42
|
-
tr.removeMark(newMark.from, newMark.to, options.type);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
// now let’s see if we can add new links
|
|
46
|
-
const nodesInChangedRanges = core.findChildrenInRange(newState.doc, newRange, (node) => node.isTextblock);
|
|
47
|
-
let textBlock;
|
|
48
|
-
let textBeforeWhitespace;
|
|
49
|
-
if (nodesInChangedRanges.length > 1) {
|
|
50
|
-
// Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter)
|
|
51
|
-
textBlock = nodesInChangedRanges[0];
|
|
52
|
-
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, " ");
|
|
53
|
-
}
|
|
54
|
-
else if (nodesInChangedRanges.length &&
|
|
55
|
-
// We want to make sure to include the block seperator argument to treat hard breaks like spaces
|
|
56
|
-
newState.doc
|
|
57
|
-
.textBetween(newRange.from, newRange.to, " ", " ")
|
|
58
|
-
.endsWith(" ")) {
|
|
59
|
-
textBlock = nodesInChangedRanges[0];
|
|
60
|
-
textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, " ");
|
|
61
|
-
}
|
|
62
|
-
if (textBlock && textBeforeWhitespace) {
|
|
63
|
-
const wordsBeforeWhitespace = textBeforeWhitespace
|
|
64
|
-
.split(" ")
|
|
65
|
-
.filter((s) => s !== "");
|
|
66
|
-
if (wordsBeforeWhitespace.length <= 0) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];
|
|
70
|
-
const lastWordAndBlockOffset = textBlock.pos +
|
|
71
|
-
textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);
|
|
72
|
-
if (!lastWordBeforeSpace) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
linkifyjs.find(lastWordBeforeSpace)
|
|
76
|
-
.filter((link) => link.isLink)
|
|
77
|
-
.filter((link) => {
|
|
78
|
-
if (options.validate) {
|
|
79
|
-
return options.validate(link.value);
|
|
80
|
-
}
|
|
81
|
-
return true;
|
|
82
|
-
})
|
|
83
|
-
// calculate link position
|
|
84
|
-
.map((link) => ({
|
|
85
|
-
...link,
|
|
86
|
-
from: lastWordAndBlockOffset + link.start + 1,
|
|
87
|
-
to: lastWordAndBlockOffset + link.end + 1,
|
|
88
|
-
}))
|
|
89
|
-
// add link mark
|
|
90
|
-
.forEach((link) => {
|
|
91
|
-
tr.addMark(link.from, link.to, options.type.create({
|
|
92
|
-
href: link.href,
|
|
93
|
-
}));
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
if (!tr.steps.length) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
return tr;
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
class Tooltip {
|
|
106
|
-
constructor(options) {
|
|
107
|
-
this.preventHide = false;
|
|
108
|
-
this.mousedownHandler = () => {
|
|
109
|
-
this.preventHide = true;
|
|
110
|
-
};
|
|
111
|
-
this.dragstartHandler = () => {
|
|
112
|
-
this.hide();
|
|
113
|
-
};
|
|
114
|
-
this.blurHandler = ({ event }) => {
|
|
115
|
-
var _a;
|
|
116
|
-
if (this.preventHide) {
|
|
117
|
-
this.preventHide = false;
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if ((event === null || event === void 0 ? void 0 : event.relatedTarget) &&
|
|
121
|
-
((_a = this.tippyWrapper.parentNode) === null || _a === void 0 ? void 0 : _a.contains(event.relatedTarget))) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
this.hide();
|
|
125
|
-
};
|
|
126
|
-
this.tippyBlurHandler = (event) => {
|
|
127
|
-
this.blurHandler({ event });
|
|
128
|
-
};
|
|
129
|
-
this.editor = options.editor;
|
|
130
|
-
this.view = this.editor.view;
|
|
131
|
-
this.tippyWrapper = document.createElement("div");
|
|
132
|
-
this.tippyWrapper.addEventListener("mousedown", this.mousedownHandler, {
|
|
133
|
-
capture: true,
|
|
134
|
-
});
|
|
135
|
-
this.view.dom.addEventListener("dragstart", this.dragstartHandler);
|
|
136
|
-
this.editor.on("blur", this.blurHandler);
|
|
137
|
-
}
|
|
138
|
-
init() {
|
|
139
|
-
this.tippyWrapper.innerHTML = "";
|
|
140
|
-
return { tippyModal: this.tippyWrapper, tippyInstance: this.tippyInstance };
|
|
141
|
-
}
|
|
142
|
-
show() {
|
|
143
|
-
var _a;
|
|
144
|
-
(_a = this.tippyInstance) === null || _a === void 0 ? void 0 : _a.show();
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
hide() {
|
|
148
|
-
setTimeout(() => { var _a; return (_a = this.tippyInstance) === null || _a === void 0 ? void 0 : _a.hide(); });
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
createTooltip() {
|
|
152
|
-
if (!this.editor || !this.editor.options)
|
|
153
|
-
return;
|
|
154
|
-
const { element: editorElement } = this.editor.options;
|
|
155
|
-
const editorIsAttached = !!editorElement.parentElement;
|
|
156
|
-
if (this.tippyInstance || !editorIsAttached) {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
this.tippyInstance = tippy(editorElement, {
|
|
160
|
-
duration: 0,
|
|
161
|
-
getReferenceClientRect: null,
|
|
162
|
-
content: this.tippyWrapper,
|
|
163
|
-
interactive: true,
|
|
164
|
-
trigger: "manual",
|
|
165
|
-
placement: "bottom",
|
|
166
|
-
hideOnClick: true,
|
|
167
|
-
onClickOutside: (instance, event) => {
|
|
168
|
-
this.hide();
|
|
169
|
-
},
|
|
170
|
-
onAfterUpdate: (instance, partialProps) => {
|
|
171
|
-
this.show();
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
if (this.tippyInstance.popper.firstChild) {
|
|
175
|
-
this.tippyInstance.popper.firstChild.addEventListener("blur", this.tippyBlurHandler);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
update(view, option = {}) {
|
|
179
|
-
var _a;
|
|
180
|
-
this.createTooltip();
|
|
181
|
-
option.arrow = (_a = option === null || option === void 0 ? void 0 : option.arrow) !== null && _a !== void 0 ? _a : false;
|
|
182
|
-
if (this.tippyInstance) {
|
|
183
|
-
this.tippyInstance.setProps({
|
|
184
|
-
...option,
|
|
185
|
-
getReferenceClientRect: () => {
|
|
186
|
-
const pos = view.state.selection.from;
|
|
187
|
-
// width: 0 is a hack to prevent tippy display in the wrong position
|
|
188
|
-
return { ...core.posToDOMRect(view, pos, pos), width: 0 };
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
return {};
|
|
193
|
-
}
|
|
194
|
-
destroyTooltip() {
|
|
195
|
-
if (this.tippyInstance) {
|
|
196
|
-
this.tippyInstance.destroy();
|
|
197
|
-
this.tippyInstance = undefined;
|
|
198
|
-
this.tippyWrapper.removeEventListener("mousedown", this.mousedownHandler, {
|
|
199
|
-
capture: true,
|
|
200
|
-
});
|
|
201
|
-
this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
|
|
202
|
-
this.editor.off("blur", this.blurHandler);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function clickHandler(options) {
|
|
208
|
-
// Create the tooltip instance
|
|
209
|
-
let tooltip = new Tooltip(options);
|
|
210
|
-
// Initialize the tooltip
|
|
211
|
-
let { tippyModal, tippyInstance } = tooltip.init();
|
|
212
|
-
return new state.Plugin({
|
|
213
|
-
key: new state.PluginKey("handleClickHyperlink"),
|
|
214
|
-
props: {
|
|
215
|
-
handleClick: (view, pos, event) => {
|
|
216
|
-
var _a, _b;
|
|
217
|
-
if (event.button !== 0)
|
|
218
|
-
return false;
|
|
219
|
-
// Get the target HTML element and its position
|
|
220
|
-
const nodeTarget = event.target;
|
|
221
|
-
const nodePos = view.posAtDOM(nodeTarget, 0);
|
|
222
|
-
// Find the closest link element to the target element
|
|
223
|
-
const link = nodeTarget === null || nodeTarget === void 0 ? void 0 : nodeTarget.closest("a");
|
|
224
|
-
// Extract attributes from the state
|
|
225
|
-
const attrs = core.getAttributes(view.state, options.type.name);
|
|
226
|
-
// Extract href and target attributes from the link element or the state
|
|
227
|
-
const href = (_a = link === null || link === void 0 ? void 0 : link.href) !== null && _a !== void 0 ? _a : attrs.href;
|
|
228
|
-
const target = (_b = link === null || link === void 0 ? void 0 : link.target) !== null && _b !== void 0 ? _b : attrs.target;
|
|
229
|
-
// If there is no previewHyperlink modal provided, then open the link in new window
|
|
230
|
-
if (!options.modal) {
|
|
231
|
-
if (link && href) {
|
|
232
|
-
window.open(href, target);
|
|
233
|
-
}
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
// if the link does not contain href attribute, hide the tooltip
|
|
237
|
-
if (!(link === null || link === void 0 ? void 0 : link.href))
|
|
238
|
-
return tooltip.hide();
|
|
239
|
-
// Create a preview of the hyperlink
|
|
240
|
-
const hyperlinkPreview = options.modal({
|
|
241
|
-
link,
|
|
242
|
-
nodePos,
|
|
243
|
-
tippy: tooltip,
|
|
244
|
-
...options,
|
|
245
|
-
});
|
|
246
|
-
// If there is no hyperlink preview, hide the modal
|
|
247
|
-
if (!hyperlinkPreview)
|
|
248
|
-
return tooltip.hide();
|
|
249
|
-
// Empty the modal and append the hyperlink preview box
|
|
250
|
-
while (tippyModal.firstChild) {
|
|
251
|
-
tippyModal.removeChild(tippyModal.firstChild);
|
|
252
|
-
}
|
|
253
|
-
tippyModal.append(hyperlinkPreview);
|
|
254
|
-
// Update the modal position
|
|
255
|
-
tooltip.update(options.editor.view);
|
|
256
|
-
return false;
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function pasteHandler(options) {
|
|
263
|
-
return new state.Plugin({
|
|
264
|
-
key: new state.PluginKey("handlePasteHyperlink"),
|
|
265
|
-
props: {
|
|
266
|
-
handlePaste: (view, event, slice) => {
|
|
267
|
-
const { state } = view;
|
|
268
|
-
const { selection } = state;
|
|
269
|
-
const { empty } = selection;
|
|
270
|
-
if (empty) {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
let textContent = "";
|
|
274
|
-
slice.content.forEach((node) => {
|
|
275
|
-
textContent += node.textContent;
|
|
276
|
-
});
|
|
277
|
-
const link = linkifyjs.find(textContent).find((item) => item.isLink && item.value === textContent);
|
|
278
|
-
if (!textContent || !link) {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
options.editor.commands.setMark(options.type, {
|
|
282
|
-
href: link.href,
|
|
283
|
-
});
|
|
284
|
-
return true;
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function editHyperlink(options) {
|
|
291
|
-
var _a;
|
|
292
|
-
const { state, dispatch } = options.editor.view;
|
|
293
|
-
const { from, to } = state.selection;
|
|
294
|
-
let link = null;
|
|
295
|
-
const selectedNode = options.editor.view.domAtPos(from - 1).node;
|
|
296
|
-
const nodeName = selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.nodeName;
|
|
297
|
-
if (nodeName === "#text") {
|
|
298
|
-
link = (_a = selectedNode.parentNode) === null || _a === void 0 ? void 0 : _a.closest("a");
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
link = selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.closest("a");
|
|
302
|
-
}
|
|
303
|
-
// If no link found and node is paragraph, try finding link in sibling/parent
|
|
304
|
-
if (!link && options.editor.view.domAtPos(from).node.nodeName === "P" && selectedNode.nextSibling)
|
|
305
|
-
link =
|
|
306
|
-
selectedNode.nextSibling ||
|
|
307
|
-
selectedNode.parentElement;
|
|
308
|
-
if (!link)
|
|
309
|
-
return true;
|
|
310
|
-
const text = options.newText || (link === null || link === void 0 ? void 0 : link.innerText);
|
|
311
|
-
// Get the position of the link in the editor view
|
|
312
|
-
const nodePos = options.editor.view.posAtDOM(link, 0);
|
|
313
|
-
const sanitizeURL = linkifyjs.find(options.newURL || link.href)
|
|
314
|
-
.filter((link) => link.isLink)
|
|
315
|
-
.filter((link) => (options.validate ? options.validate(link.value) : true))
|
|
316
|
-
.at(0);
|
|
317
|
-
return options.editor
|
|
318
|
-
.chain()
|
|
319
|
-
.focus()
|
|
320
|
-
.command(({ tr }) => {
|
|
321
|
-
var _a;
|
|
322
|
-
tr.deleteRange(nodePos, nodePos + ((_a = ((link === null || link === void 0 ? void 0 : link.innerText) || "")) === null || _a === void 0 ? void 0 : _a.length)).replaceWith(nodePos, nodePos, options.editor.schema.text(text, [
|
|
323
|
-
options.editor.schema.marks.hyperlink.create({
|
|
324
|
-
href: sanitizeURL === null || sanitizeURL === void 0 ? void 0 : sanitizeURL.href,
|
|
325
|
-
}),
|
|
326
|
-
]));
|
|
327
|
-
return true;
|
|
328
|
-
})
|
|
329
|
-
.run();
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const Hyperlink = core.Mark.create({
|
|
333
|
-
name: "hyperlink",
|
|
334
|
-
priority: 1000,
|
|
335
|
-
keepOnSplit: false,
|
|
336
|
-
onCreate() {
|
|
337
|
-
this.options.protocols.forEach((protocol) => {
|
|
338
|
-
if (typeof protocol === "string") {
|
|
339
|
-
linkifyjs.registerCustomProtocol(protocol);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
linkifyjs.registerCustomProtocol(protocol.scheme, protocol.optionalSlashes);
|
|
343
|
-
});
|
|
344
|
-
},
|
|
345
|
-
onDestroy() {
|
|
346
|
-
linkifyjs.reset();
|
|
347
|
-
},
|
|
348
|
-
inclusive() {
|
|
349
|
-
return this.options.autoHyperlink;
|
|
350
|
-
},
|
|
351
|
-
addOptions() {
|
|
352
|
-
return {
|
|
353
|
-
openOnClick: true,
|
|
354
|
-
hyperlinkOnPaste: true,
|
|
355
|
-
autoHyperlink: true,
|
|
356
|
-
protocols: [],
|
|
357
|
-
HTMLAttributes: {
|
|
358
|
-
target: "_blank",
|
|
359
|
-
rel: "noopener noreferrer nofollow",
|
|
360
|
-
class: null,
|
|
361
|
-
},
|
|
362
|
-
modals: {
|
|
363
|
-
previewHyperlink: null,
|
|
364
|
-
setHyperlink: null,
|
|
365
|
-
},
|
|
366
|
-
validate: undefined,
|
|
367
|
-
};
|
|
368
|
-
},
|
|
369
|
-
addAttributes() {
|
|
370
|
-
return {
|
|
371
|
-
href: {
|
|
372
|
-
default: null,
|
|
373
|
-
},
|
|
374
|
-
target: {
|
|
375
|
-
default: this.options.HTMLAttributes.target,
|
|
376
|
-
},
|
|
377
|
-
class: {
|
|
378
|
-
default: this.options.HTMLAttributes.class,
|
|
379
|
-
},
|
|
380
|
-
};
|
|
381
|
-
},
|
|
382
|
-
parseHTML() {
|
|
383
|
-
return [{ tag: 'a[href]:not([href *= "javascript:" i])' }];
|
|
384
|
-
},
|
|
385
|
-
renderHTML({ HTMLAttributes }) {
|
|
386
|
-
return ["a", core.mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
387
|
-
},
|
|
388
|
-
addCommands() {
|
|
389
|
-
return {
|
|
390
|
-
setHyperlink: (attributes) => ({ editor, chain }) => {
|
|
391
|
-
if (!this.options.modals.setHyperlink) {
|
|
392
|
-
return chain()
|
|
393
|
-
.setMark(this.name, attributes)
|
|
394
|
-
.setMeta("preventAutohyperlink", true)
|
|
395
|
-
.run();
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
this.options.modals.setHyperlink({
|
|
399
|
-
editor,
|
|
400
|
-
validate: this.options.validate,
|
|
401
|
-
extentionName: this.name,
|
|
402
|
-
attributes,
|
|
403
|
-
Tooltip,
|
|
404
|
-
roundArrow: tippy.roundArrow,
|
|
405
|
-
});
|
|
406
|
-
return true;
|
|
407
|
-
}
|
|
408
|
-
},
|
|
409
|
-
editHyperlink: (attributes) => ({ editor }) => {
|
|
410
|
-
return editHyperlink({
|
|
411
|
-
...attributes,
|
|
412
|
-
editor,
|
|
413
|
-
validate: this.options.validate,
|
|
414
|
-
});
|
|
415
|
-
},
|
|
416
|
-
editHyperLinkText: (newText) => ({ editor }) => {
|
|
417
|
-
return editHyperlink({
|
|
418
|
-
editor,
|
|
419
|
-
newText,
|
|
420
|
-
validate: this.options.validate,
|
|
421
|
-
});
|
|
422
|
-
},
|
|
423
|
-
editHyperLinkHref: (newURL) => ({ editor }) => {
|
|
424
|
-
return editHyperlink({
|
|
425
|
-
editor,
|
|
426
|
-
validate: this.options.validate,
|
|
427
|
-
newURL,
|
|
428
|
-
});
|
|
429
|
-
},
|
|
430
|
-
unsetHyperlink: () => ({ chain }) => {
|
|
431
|
-
return chain()
|
|
432
|
-
.unsetMark(this.name, { extendEmptyMarkRange: true })
|
|
433
|
-
.setMeta("preventAutohyperlink", true)
|
|
434
|
-
.run();
|
|
435
|
-
},
|
|
436
|
-
};
|
|
437
|
-
},
|
|
438
|
-
addPasteRules() {
|
|
439
|
-
return [
|
|
440
|
-
core.markPasteRule({
|
|
441
|
-
find: (text) => linkifyjs.find(text)
|
|
442
|
-
.filter((link) => {
|
|
443
|
-
if (this.options.validate) {
|
|
444
|
-
return this.options.validate(link.value);
|
|
445
|
-
}
|
|
446
|
-
return true;
|
|
447
|
-
})
|
|
448
|
-
.filter((link) => link.isLink)
|
|
449
|
-
.map((link) => ({
|
|
450
|
-
text: link.value,
|
|
451
|
-
index: link.start,
|
|
452
|
-
data: link,
|
|
453
|
-
})),
|
|
454
|
-
type: this.type,
|
|
455
|
-
getAttributes: (match) => {
|
|
456
|
-
var _a;
|
|
457
|
-
return ({
|
|
458
|
-
href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
|
|
459
|
-
});
|
|
460
|
-
},
|
|
461
|
-
}),
|
|
462
|
-
];
|
|
463
|
-
},
|
|
464
|
-
addProseMirrorPlugins() {
|
|
465
|
-
const plugins = [];
|
|
466
|
-
if (this.options.autoHyperlink) {
|
|
467
|
-
plugins.push(autoHyperlink({
|
|
468
|
-
type: this.type,
|
|
469
|
-
validate: this.options.validate,
|
|
470
|
-
}));
|
|
471
|
-
}
|
|
472
|
-
if (this.options.openOnClick) {
|
|
473
|
-
plugins.push(clickHandler({
|
|
474
|
-
type: this.type,
|
|
475
|
-
editor: this.editor,
|
|
476
|
-
validate: this.options.validate,
|
|
477
|
-
modal: this.options.modals.previewHyperlink,
|
|
478
|
-
}));
|
|
479
|
-
}
|
|
480
|
-
if (this.options.hyperlinkOnPaste) {
|
|
481
|
-
plugins.push(pasteHandler({
|
|
482
|
-
editor: this.editor,
|
|
483
|
-
type: this.type,
|
|
484
|
-
}));
|
|
485
|
-
}
|
|
486
|
-
return plugins;
|
|
487
|
-
},
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
const editHyperlinkHandler = (options) => {
|
|
491
|
-
var _a;
|
|
492
|
-
// Create the form
|
|
493
|
-
const form = document.createElement("form");
|
|
494
|
-
const buttonsWrapper = document.createElement("div");
|
|
495
|
-
const inputsWrapper = document.createElement("div");
|
|
496
|
-
const linkTextInput = document.createElement("input");
|
|
497
|
-
const hrefInput = document.createElement("input");
|
|
498
|
-
const applyButton = document.createElement("button");
|
|
499
|
-
buttonsWrapper.classList.add("hyperlink-edit-modal__buttons-wrapper");
|
|
500
|
-
inputsWrapper.classList.add("hyperlink-edit-modal__inputs-wrapper");
|
|
501
|
-
linkTextInput.type = "text";
|
|
502
|
-
linkTextInput.value = ((_a = options.link) === null || _a === void 0 ? void 0 : _a.innerText) || "";
|
|
503
|
-
linkTextInput.placeholder = "Enter link text";
|
|
504
|
-
hrefInput.type = "text";
|
|
505
|
-
hrefInput.value = options.link.href;
|
|
506
|
-
hrefInput.placeholder = "Enter href";
|
|
507
|
-
applyButton.type = "submit";
|
|
508
|
-
applyButton.classList.add("hyperlink-edit-modal__apply-button");
|
|
509
|
-
applyButton.innerText = "Apply";
|
|
510
|
-
buttonsWrapper.append(applyButton);
|
|
511
|
-
inputsWrapper.append(linkTextInput, hrefInput);
|
|
512
|
-
// Append to form
|
|
513
|
-
form.append(inputsWrapper, buttonsWrapper);
|
|
514
|
-
options.hyperlinkModal.innerHTML = "";
|
|
515
|
-
options.hyperlinkModal.appendChild(form);
|
|
516
|
-
hrefInput.addEventListener("keydown", () => {
|
|
517
|
-
hrefInput.style.outlineColor = " #dadce0";
|
|
518
|
-
});
|
|
519
|
-
hrefInput.addEventListener("keydown", () => {
|
|
520
|
-
linkTextInput.style.outlineColor = " #dadce0";
|
|
521
|
-
});
|
|
522
|
-
// Handle form submission
|
|
523
|
-
form.addEventListener("submit", (e) => {
|
|
524
|
-
e.preventDefault();
|
|
525
|
-
const newLinkText = linkTextInput.value;
|
|
526
|
-
const newHref = hrefInput.value;
|
|
527
|
-
if (!newHref || !newLinkText) {
|
|
528
|
-
if (!newHref)
|
|
529
|
-
hrefInput.style.outlineColor = "red";
|
|
530
|
-
if (!newLinkText)
|
|
531
|
-
linkTextInput.style.outlineColor = "red";
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
const sanitizeURL = linkifyjs.find(newHref)
|
|
535
|
-
.filter((link) => link.isLink)
|
|
536
|
-
.filter((link) => {
|
|
537
|
-
if (options.validate) {
|
|
538
|
-
return options.validate(link.value);
|
|
539
|
-
}
|
|
540
|
-
return true;
|
|
541
|
-
})
|
|
542
|
-
.at(0);
|
|
543
|
-
if (!(sanitizeURL === null || sanitizeURL === void 0 ? void 0 : sanitizeURL.href)) {
|
|
544
|
-
hrefInput.style.outlineColor = "red";
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
options.editor.chain().focus().extendMarkRange("hyperlink").editHyperlink({
|
|
548
|
-
newURL: newHref,
|
|
549
|
-
newText: newLinkText,
|
|
550
|
-
});
|
|
551
|
-
options.tippy.hide();
|
|
552
|
-
});
|
|
553
|
-
// Show tooltip
|
|
554
|
-
options.tippy.update(options.editor.view, {
|
|
555
|
-
arrow: tippy.roundArrow,
|
|
556
|
-
});
|
|
557
|
-
setTimeout(() => linkTextInput.focus());
|
|
558
|
-
};
|
|
559
|
-
|
|
560
|
-
// prettier-ignore
|
|
561
|
-
const Copy = ({ size = 24, fill = 'rgb(104, 81, 255)' } = {}) => {
|
|
562
|
-
return `
|
|
563
|
-
<svg fill=${fill} viewBox="0 0 24 24" width=${size} xmlns="http://www.w3.org/2000/svg">
|
|
564
|
-
<path d="m13 20a5.006 5.006 0 0 0 5-5v-8.757a3.972 3.972 0 0 0 -1.172-2.829l-2.242-2.242a3.972 3.972 0 0 0 -2.829-1.172h-4.757a5.006 5.006 0 0 0 -5 5v10a5.006 5.006 0 0 0 5 5zm-9-5v-10a3 3 0 0 1 3-3s4.919.014 5 .024v1.976a2 2 0 0 0 2 2h1.976c.01.081.024 9 .024 9a3 3 0 0 1 -3 3h-6a3 3 0 0 1 -3-3zm18-7v11a5.006 5.006 0 0 1 -5 5h-9a1 1 0 0 1 0-2h9a3 3 0 0 0 3-3v-11a1 1 0 0 1 2 0z"/>
|
|
565
|
-
</svg>
|
|
566
|
-
`;
|
|
567
|
-
};
|
|
568
|
-
const LinkSlash = ({ size = 24, fill = "rgb(104, 81, 255)", } = {}) => {
|
|
569
|
-
return `
|
|
570
|
-
<svg fill=${fill} viewBox="0 0 24 24" width=${size} xmlns="http://www.w3.org/2000/svg">
|
|
571
|
-
<path d="M11.953,17.993c-1.557-.074-3.021-.722-4.121-1.823-.23-.23-.446-.482-.641-.748-.325-.446-.227-1.072,.22-1.397,.446-.325,1.071-.227,1.397,.219,.129,.178,.274,.349,.437,.511,.749,.75,1.744,1.19,2.802,1.24,.552,.026,.979,.495,.952,1.046-.025,.536-.468,.953-.998,.953h-.048ZM1.831,13.331C.65,14.511,0,16.081,0,17.75c0,1.669,.65,3.239,1.831,4.419,1.18,1.181,2.749,1.831,4.419,1.831s3.239-.65,4.419-1.831l1.058-1.058c.391-.391,.391-1.023,0-1.414s-1.023-.391-1.414,0l-1.058,1.058c-.803,.803-1.87,1.245-3.005,1.245s-2.202-.442-3.005-1.245-1.245-1.87-1.245-3.005,.442-2.203,1.245-3.005l3.037-3.038c.391-.391,.391-1.024,0-1.414-.391-.391-1.023-.391-1.414,0l-3.037,3.038ZM.293,1.707L22.293,23.707c.195,.195,.451,.293,.707,.293s.512-.098,.707-.293c.391-.391,.391-1.023,0-1.414l-6.581-6.581,5.043-5.043c1.181-1.18,1.831-2.75,1.831-4.419,0-1.669-.65-3.239-1.831-4.419C20.989,.65,19.42,0,17.75,0s-3.239,.65-4.419,1.831l-1.058,1.058c-.391,.391-.391,1.023,0,1.414s1.023,.391,1.414,0l1.058-1.058c.803-.803,1.87-1.245,3.005-1.245s2.202,.442,3.005,1.245,1.245,1.87,1.245,3.005-.442,2.203-1.245,3.005l-5.043,5.043-5.852-5.852c1.612-.808,3.575-.52,4.894,.798,.162,.162,.308,.333,.437,.511,.196,.269,.501,.411,.81,.411,.204,0,.41-.063,.588-.192,.446-.325,.545-.951,.22-1.397-.194-.266-.41-.518-.641-.748-2.095-2.095-5.297-2.408-7.76-.835L1.707,.293c-.391-.391-1.023-.391-1.414,0s-.391,1.023,0,1.414Z"/>
|
|
572
|
-
</svg>
|
|
573
|
-
`;
|
|
574
|
-
};
|
|
575
|
-
const Pencil = ({ size = 24, fill = "rgb(104, 81, 255)", } = {}) => {
|
|
576
|
-
return `
|
|
577
|
-
<svg fill=${fill} viewBox="0 0 24 24" width=${size} xmlns="http://www.w3.org/2000/svg">
|
|
578
|
-
<path d="M22.853,1.148a3.626,3.626,0,0,0-5.124,0L1.465,17.412A4.968,4.968,0,0,0,0,20.947V23a1,1,0,0,0,1,1H3.053a4.966,4.966,0,0,0,3.535-1.464L22.853,6.271A3.626,3.626,0,0,0,22.853,1.148ZM5.174,21.122A3.022,3.022,0,0,1,3.053,22H2V20.947a2.98,2.98,0,0,1,.879-2.121L15.222,6.483l2.3,2.3ZM21.438,4.857,18.932,7.364l-2.3-2.295,2.507-2.507a1.623,1.623,0,1,1,2.295,2.3Z"/>
|
|
579
|
-
</svg>
|
|
580
|
-
`;
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
function previewHyperlinkModal(options) {
|
|
584
|
-
const href = options.link.href;
|
|
585
|
-
const hyperlinkModal = document.createElement("div");
|
|
586
|
-
const removeButton = document.createElement("button");
|
|
587
|
-
const copyButton = document.createElement("button");
|
|
588
|
-
const editButton = document.createElement("button");
|
|
589
|
-
const newBubble = document.createElement("div");
|
|
590
|
-
newBubble.classList.add("hyperlink-preview-modal__metadata");
|
|
591
|
-
const hrefTitle = document.createElement("a");
|
|
592
|
-
hrefTitle.setAttribute("target", "_blank");
|
|
593
|
-
hrefTitle.setAttribute("rel", "noreferrer");
|
|
594
|
-
hrefTitle.setAttribute("href", href);
|
|
595
|
-
hrefTitle.innerText = href;
|
|
596
|
-
newBubble.append(hrefTitle);
|
|
597
|
-
fetch("/api/metadata", {
|
|
598
|
-
method: "POST",
|
|
599
|
-
headers: { "Content-Type": "application/json" },
|
|
600
|
-
body: JSON.stringify({ url: href }),
|
|
601
|
-
})
|
|
602
|
-
.then((response) => response.json())
|
|
603
|
-
.then((data) => {
|
|
604
|
-
// Create a new bubble with the title
|
|
605
|
-
hrefTitle.setAttribute("href", href);
|
|
606
|
-
hrefTitle.innerText = data.title || data["og:title"] || href;
|
|
607
|
-
newBubble.replaceChildren(hrefTitle);
|
|
608
|
-
// Create an image element if image exists in metadata
|
|
609
|
-
if (data.icon || data.image || data["og:image"]) {
|
|
610
|
-
const img = document.createElement("img");
|
|
611
|
-
img.src = data.icon || data.image || data["og:image"];
|
|
612
|
-
img.alt = data.title || data["og:title"] || "hyperlink image";
|
|
613
|
-
newBubble.appendChild(img);
|
|
614
|
-
}
|
|
615
|
-
})
|
|
616
|
-
.catch((error) => {
|
|
617
|
-
console.error("Error fetching metadata:", error);
|
|
618
|
-
});
|
|
619
|
-
hyperlinkModal.classList.add("hyperlink-preview-modal");
|
|
620
|
-
removeButton.classList.add("hyperlink-preview-modal__remove-button");
|
|
621
|
-
removeButton.innerHTML = LinkSlash();
|
|
622
|
-
editButton.classList.add("hyperlink-preview-modal__edit-button");
|
|
623
|
-
editButton.innerHTML = Pencil();
|
|
624
|
-
copyButton.classList.add("hyperlink-preview-modal__copy-button");
|
|
625
|
-
copyButton.innerHTML = Copy();
|
|
626
|
-
removeButton.addEventListener("click", () => {
|
|
627
|
-
options.tippy.hide();
|
|
628
|
-
return options.editor.chain().focus().unsetHyperlink().run();
|
|
629
|
-
});
|
|
630
|
-
editButton.addEventListener("click", () => editHyperlinkHandler({ ...options, hyperlinkModal }));
|
|
631
|
-
copyButton.addEventListener("click", () => {
|
|
632
|
-
options.tippy.hide();
|
|
633
|
-
navigator.clipboard.writeText(href);
|
|
634
|
-
});
|
|
635
|
-
hyperlinkModal.append(newBubble, copyButton, editButton, removeButton);
|
|
636
|
-
return hyperlinkModal;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
let tooltip = undefined;
|
|
640
|
-
function setHyperlinkModal(options) {
|
|
641
|
-
// Create the tooltip instance
|
|
642
|
-
if (!tooltip)
|
|
643
|
-
tooltip = new options.Tooltip(options);
|
|
644
|
-
// Initialize the tooltip
|
|
645
|
-
let { tippyModal } = tooltip.init();
|
|
646
|
-
const hyperlinkModal = document.createElement("div");
|
|
647
|
-
const buttonsWrapper = document.createElement("div");
|
|
648
|
-
const inputsWrapper = document.createElement("div");
|
|
649
|
-
hyperlinkModal.classList.add("hyperlink-set-modal");
|
|
650
|
-
buttonsWrapper.classList.add("hyperlink-set-modal__buttons-wrapper");
|
|
651
|
-
inputsWrapper.classList.add("hyperlink-set-modal__inputs-wrapper");
|
|
652
|
-
// create a form that contain url input and a button for submit
|
|
653
|
-
const form = document.createElement("form");
|
|
654
|
-
const input = document.createElement("input");
|
|
655
|
-
const button = document.createElement("button");
|
|
656
|
-
input.setAttribute("type", "text");
|
|
657
|
-
input.setAttribute("placeholder", "https://example.com");
|
|
658
|
-
button.setAttribute("type", "submit");
|
|
659
|
-
button.innerText = "Submit";
|
|
660
|
-
inputsWrapper.append(input);
|
|
661
|
-
buttonsWrapper.append(button);
|
|
662
|
-
form.append(inputsWrapper, buttonsWrapper);
|
|
663
|
-
hyperlinkModal.append(form);
|
|
664
|
-
tippyModal.innerHTML = "";
|
|
665
|
-
tippyModal.append(hyperlinkModal);
|
|
666
|
-
tooltip.update(options.editor.view, {
|
|
667
|
-
arrow: options.roundArrow,
|
|
668
|
-
});
|
|
669
|
-
// make sure
|
|
670
|
-
setTimeout(() => {
|
|
671
|
-
input.focus();
|
|
672
|
-
input.style.outlineColor = " #dadce0";
|
|
673
|
-
}, 100);
|
|
674
|
-
input.addEventListener("keydown", () => {
|
|
675
|
-
input.style.outlineColor = " #dadce0";
|
|
676
|
-
});
|
|
677
|
-
// event listenr for submit button
|
|
678
|
-
form.addEventListener("submit", (event) => {
|
|
679
|
-
event.preventDefault();
|
|
680
|
-
const url = input.value;
|
|
681
|
-
if (!url) {
|
|
682
|
-
input.style.outlineColor = "red";
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
const sanitizeURL = linkifyjs.find(url)
|
|
686
|
-
.filter((link) => link.isLink)
|
|
687
|
-
.filter((link) => {
|
|
688
|
-
if (options.validate) {
|
|
689
|
-
return options.validate(link.value);
|
|
690
|
-
}
|
|
691
|
-
return true;
|
|
692
|
-
})
|
|
693
|
-
.at(0);
|
|
694
|
-
if (!(sanitizeURL === null || sanitizeURL === void 0 ? void 0 : sanitizeURL.href)) {
|
|
695
|
-
input.style.outlineColor = "red";
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
tooltip === null || tooltip === void 0 ? void 0 : tooltip.hide();
|
|
699
|
-
return options.editor
|
|
700
|
-
.chain()
|
|
701
|
-
.setMark(options.extentionName, { href: sanitizeURL.href })
|
|
702
|
-
.setMeta("preventautohyperlink", true)
|
|
703
|
-
.run();
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
exports.Hyperlink = Hyperlink;
|
|
708
|
-
exports.Tooltip = Tooltip;
|
|
709
|
-
exports.previewHyperlinkModal = previewHyperlinkModal;
|
|
710
|
-
exports.setHyperlinkModal = setHyperlinkModal;
|
|
711
|
-
|
|
712
|
-
}));
|
|
713
|
-
//# sourceMappingURL=index.umd.js.map
|