-
Notifications
You must be signed in to change notification settings - Fork 138
/
Copy pathindex.ts
99 lines (81 loc) · 3.28 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import React, { useCallback } from 'react';
import DOMPurify from 'dompurify';
import { insertTemplateToDOM } from './insertTemplate';
import { sanitizeString } from '../../utils';
import { DynamicProps } from './types';
import { domToMessageTemplate, getLeafNodes, getUsersFromWords, hasMention } from './utils';
function pasteContentAtCaret(content: string) {
const selection = window.getSelection(); // Get the current selection
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(selection.rangeCount - 1); // Get the last range
range.deleteContents(); // Clear any existing content
// Create a new text node with the content and a Zero-width space
const textNode = document.createTextNode(content + '\u200B');
range.insertNode(textNode); // Insert the new text node at the caret position
// Move the caret to the end of the inserted content
range.setStart(textNode, textNode.length);
range.collapse(true); // Collapse the range (no text selection)
// Reset the selection with the updated range
selection.removeAllRanges();
selection.addRange(range); // Apply the updated selection
}
}
function createPasteNodeWithContent(html: string): HTMLDivElement {
const pasteNode = document.createElement('div');
pasteNode.innerHTML = html;
return pasteNode;
}
// usePaste Hook
export function usePaste({
ref,
setIsInput,
channel,
setMentionedUsers,
}: DynamicProps): (e: React.ClipboardEvent<HTMLDivElement>) => void {
return useCallback((e) => {
e.preventDefault();
const html = e.clipboardData.getData('text/html');
const text = e.clipboardData.getData('text') || getURIListText(e);
// 1. Simple text paste: no HTML present
if (!html) {
pasteContentAtCaret(sanitizeString(text));
setIsInput(true);
return;
}
// 2. HTML paste: process mentions and sanitized content
const purifier = DOMPurify(window);
const cleanHtml = purifier.sanitize(html);
const pasteNode = createPasteNodeWithContent(cleanHtml);
if (!hasMention(pasteNode)) {
// No mention, paste as plain text
pasteContentAtCaret(sanitizeString(text));
pasteNode.remove();
setIsInput(true);
return;
}
// 3. Mentions present: process mentions and update state
const leafNodes = getLeafNodes(pasteNode);
const words = domToMessageTemplate(leafNodes);
const mentionedUsers = channel.isGroupChannel() ? getUsersFromWords(words, channel) : [];
setMentionedUsers(mentionedUsers); // Update mentioned users state
insertTemplateToDOM(words); // Insert mentions and content into the DOM
pasteNode.remove();
setIsInput(true);
}, [ref, setIsInput, channel, setMentionedUsers]);
}
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#dragging_links
function getURIListText(e: React.ClipboardEvent<HTMLDivElement>) {
const pasteData = e.clipboardData.getData('text/uri-list');
if (pasteData.length === 0) return '';
return pasteData
.split('\n')
.reduce((accumulator, line) => {
const txt = line.trim();
if (txt !== '' && !txt.startsWith('#')) {
accumulator += txt + '\n';
}
return accumulator;
}, '');
}
// to do -> In the future don't export default
export default usePaste;