@@ -4,16 +4,36 @@ import DOMPurify from 'dompurify';
4
4
import { inserTemplateToDOM } from './insertTemplate' ;
5
5
import { sanitizeString } from '../../utils' ;
6
6
import { DynamicProps } from './types' ;
7
- import { createPasteNode , domToMessageTemplate , extractTextFromNodes , getLeafNodes , getUsersFromWords , hasMention } from './utils' ;
8
-
9
- // exported, should be backward compatible
10
- // conditions to test:
11
- // 1. paste simple text
12
- // 2. paste text with mention
13
- // 3. paste text with mention and text
14
- // 4. paste text with mention and text and paste again before and after
15
- // 5. copy message with mention(only one mention, no other text) and paste
16
- // 6. copy message with mention from input and paste(before and after)
7
+ import { domToMessageTemplate , extractTextFromNodes , getLeafNodes , getUsersFromWords , hasMention } from './utils' ;
8
+
9
+ function pasteContentAtCaret ( content : string ) {
10
+ const selection = window . getSelection ( ) ; // Get the current selection
11
+ if ( selection && selection . rangeCount > 0 ) {
12
+ const range = selection . getRangeAt ( selection . rangeCount - 1 ) ; // Get the last range
13
+
14
+ range . deleteContents ( ) ; // Clear any existing content
15
+
16
+ // Create a new text node with the content and a Zero-width space
17
+ const textNode = document . createTextNode ( content + '\u200B' ) ;
18
+ range . insertNode ( textNode ) ; // Insert the new text node at the caret position
19
+
20
+ // Move the caret to the end of the inserted content
21
+ range . setStart ( textNode , textNode . length ) ;
22
+ range . collapse ( true ) ; // Collapse the range (no text selection)
23
+
24
+ // Reset the selection with the updated range
25
+ selection . removeAllRanges ( ) ;
26
+ selection . addRange ( range ) ; // Apply the updated selection
27
+ }
28
+ }
29
+
30
+ function createPasteNodeWithContent ( html : string ) : HTMLDivElement {
31
+ const pasteNode = document . createElement ( 'div' ) ;
32
+ pasteNode . innerHTML = html ;
33
+ return pasteNode ;
34
+ }
35
+
36
+ // usePaste Hook
17
37
export function usePaste ( {
18
38
ref,
19
39
setIsInput,
@@ -22,42 +42,40 @@ export function usePaste({
22
42
} : DynamicProps ) : ( e : React . ClipboardEvent < HTMLDivElement > ) => void {
23
43
return useCallback ( ( e ) => {
24
44
e . preventDefault ( ) ;
45
+
25
46
const html = e . clipboardData . getData ( 'text/html' ) ;
26
- // simple text, continue as normal
47
+ const text = e . clipboardData . getData ( 'text' ) || getURIListText ( e ) ;
48
+
49
+ // 1. Simple text paste: no HTML present
27
50
if ( ! html ) {
28
- const text = e . clipboardData . getData ( 'text' ) || getURIListText ( e ) ;
29
- document . execCommand ( 'insertText' , false , sanitizeString ( text ) ) ;
51
+ pasteContentAtCaret ( sanitizeString ( text ) ) ;
30
52
setIsInput ( true ) ;
31
53
return ;
32
54
}
33
55
34
- // has html, check if there are mentions, sanitize and insert
56
+ // 2. HTML paste: process mentions and sanitized content
35
57
const purifier = DOMPurify ( window ) ;
36
- const clean = purifier . sanitize ( html ) ;
37
- const pasteNode = createPasteNode ( ) ;
38
- if ( pasteNode ) {
39
- pasteNode . innerHTML = clean ;
40
- // does not have mention, continue as normal
41
- if ( ! hasMention ( pasteNode ) ) {
42
- // to preserve space between words
43
- const text = extractTextFromNodes ( Array . from ( pasteNode . children ) as HTMLSpanElement [ ] ) ;
44
- document . execCommand ( 'insertText' , false , sanitizeString ( text ) ) ;
45
- pasteNode . remove ( ) ;
46
- setIsInput ( true ) ;
47
- return ;
48
- }
49
-
50
- // has mention, collect leaf nodes and parse words
51
- const leafNodes = getLeafNodes ( pasteNode ) ;
52
- const words = domToMessageTemplate ( leafNodes ) ;
53
- const mentionedUsers = channel . isGroupChannel ( ) ? getUsersFromWords ( words , channel ) : [ ] ;
58
+ const cleanHtml = purifier . sanitize ( html ) ;
59
+ const pasteNode = createPasteNodeWithContent ( cleanHtml ) ;
54
60
55
- // side effects
56
- setMentionedUsers ( mentionedUsers ) ;
57
- inserTemplateToDOM ( words ) ;
61
+ if ( ! hasMention ( pasteNode ) ) {
62
+ // No mention, paste as plain text
63
+ const extractedText = extractTextFromNodes ( Array . from ( pasteNode . children ) as HTMLSpanElement [ ] ) ;
64
+ pasteContentAtCaret ( sanitizeString ( extractedText ) ) ;
58
65
pasteNode . remove ( ) ;
66
+ setIsInput ( true ) ;
67
+ return ;
59
68
}
60
69
70
+ // 3. Mentions present: process mentions and update state
71
+ const leafNodes = getLeafNodes ( pasteNode ) ;
72
+ const words = domToMessageTemplate ( leafNodes ) ;
73
+ const mentionedUsers = channel . isGroupChannel ( ) ? getUsersFromWords ( words , channel ) : [ ] ;
74
+
75
+ setMentionedUsers ( mentionedUsers ) ; // Update mentioned users state
76
+ inserTemplateToDOM ( words ) ; // Insert mentions and content into the DOM
77
+ pasteNode . remove ( ) ;
78
+
61
79
setIsInput ( true ) ;
62
80
} , [ ref , setIsInput , channel , setMentionedUsers ] ) ;
63
81
}
@@ -78,5 +96,5 @@ function getURIListText(e: React.ClipboardEvent<HTMLDivElement>) {
78
96
} , '' ) ;
79
97
}
80
98
81
- // to do -> In the future donot export default
99
+ // to do -> In the future don't export default
82
100
export default usePaste ;
0 commit comments