1
1
import Clipboard from '@react-native-clipboard/clipboard' ;
2
- import lodashGet from 'lodash/get' ;
3
2
import * as Browser from '@libs/Browser' ;
4
3
import CONST from '@src/CONST' ;
4
+ import { CanSetHtml , SetHtml , SetString } from './types' ;
5
5
6
- const canSetHtml = ( ) => lodashGet ( navigator , 'clipboard.write' ) ;
6
+ type ComposerSelection = {
7
+ start : number ;
8
+ end : number ;
9
+ direction : 'forward' | 'backward' | 'none' ;
10
+ } ;
11
+
12
+ type AnchorSelection = {
13
+ anchorOffset : number ;
14
+ focusOffset : number ;
15
+ anchorNode : Node ;
16
+ focusNode : Node ;
17
+ } ;
18
+
19
+ type NullableObject < T > = { [ K in keyof T ] : T [ K ] | null } ;
20
+
21
+ type OriginalSelection = ComposerSelection | NullableObject < AnchorSelection > ;
22
+
23
+ const canSetHtml : CanSetHtml =
24
+ ( ) =>
25
+ ( ...args : ClipboardItems ) =>
26
+ navigator ?. clipboard ?. write ( [ ...args ] ) ;
7
27
8
28
/**
9
29
* Deprecated method to write the content as HTML to clipboard.
10
- * @param {String } html HTML representation
11
- * @param {String } text Plain text representation
12
30
*/
13
- function setHTMLSync ( html , text ) {
31
+ function setHTMLSync ( html : string , text : string ) {
14
32
const node = document . createElement ( 'span' ) ;
15
33
node . textContent = html ;
16
34
node . style . all = 'unset' ;
@@ -21,16 +39,21 @@ function setHTMLSync(html, text) {
21
39
node . addEventListener ( 'copy' , ( e ) => {
22
40
e . stopPropagation ( ) ;
23
41
e . preventDefault ( ) ;
24
- e . clipboardData . clearData ( ) ;
25
- e . clipboardData . setData ( 'text/html' , html ) ;
26
- e . clipboardData . setData ( 'text/plain' , text ) ;
42
+ e . clipboardData ? .clearData ( ) ;
43
+ e . clipboardData ? .setData ( 'text/html' , html ) ;
44
+ e . clipboardData ? .setData ( 'text/plain' , text ) ;
27
45
} ) ;
28
46
document . body . appendChild ( node ) ;
29
47
30
- const selection = window . getSelection ( ) ;
31
- const firstAnchorChild = selection . anchorNode && selection . anchorNode . firstChild ;
48
+ const selection = window ?. getSelection ( ) ;
49
+
50
+ if ( selection === null ) {
51
+ return ;
52
+ }
53
+
54
+ const firstAnchorChild = selection . anchorNode ?. firstChild ;
32
55
const isComposer = firstAnchorChild instanceof HTMLTextAreaElement ;
33
- let originalSelection = null ;
56
+ let originalSelection : OriginalSelection | null = null ;
34
57
if ( isComposer ) {
35
58
originalSelection = {
36
59
start : firstAnchorChild . selectionStart ,
@@ -60,23 +83,23 @@ function setHTMLSync(html, text) {
60
83
61
84
selection . removeAllRanges ( ) ;
62
85
63
- if ( isComposer ) {
86
+ const anchorSelection = originalSelection as AnchorSelection ;
87
+
88
+ if ( isComposer && 'start' in originalSelection ) {
64
89
firstAnchorChild . setSelectionRange ( originalSelection . start , originalSelection . end , originalSelection . direction ) ;
65
- } else if ( originalSelection . anchorNode && originalSelection . focusNode ) {
90
+ } else if ( anchorSelection . anchorNode && anchorSelection . focusNode ) {
66
91
// When copying to the clipboard here, the original values of anchorNode and focusNode will be null since there will be no user selection.
67
92
// We are adding a check to prevent null values from being passed to setBaseAndExtent, in accordance with the standards of the Selection API as outlined here: https://w3c.github.io/selection-api/#dom-selection-setbaseandextent.
68
- selection . setBaseAndExtent ( originalSelection . anchorNode , originalSelection . anchorOffset , originalSelection . focusNode , originalSelection . focusOffset ) ;
93
+ selection . setBaseAndExtent ( anchorSelection . anchorNode , anchorSelection . anchorOffset , anchorSelection . focusNode , anchorSelection . focusOffset ) ;
69
94
}
70
95
71
96
document . body . removeChild ( node ) ;
72
97
}
73
98
74
99
/**
75
100
* Writes the content as HTML if the web client supports it.
76
- * @param {String } html HTML representation
77
- * @param {String } text Plain text representation
78
101
*/
79
- const setHtml = ( html , text ) => {
102
+ const setHtml : SetHtml = ( html : string , text : string ) => {
80
103
if ( ! html || ! text ) {
81
104
return ;
82
105
}
@@ -93,8 +116,8 @@ const setHtml = (html, text) => {
93
116
setHTMLSync ( html , text ) ;
94
117
} else {
95
118
navigator . clipboard . write ( [
96
- // eslint-disable-next-line no-undef
97
119
new ClipboardItem ( {
120
+ /* eslint-disable @typescript-eslint/naming-convention */
98
121
'text/html' : new Blob ( [ html ] , { type : 'text/html' } ) ,
99
122
'text/plain' : new Blob ( [ text ] , { type : 'text/plain' } ) ,
100
123
} ) ,
@@ -104,10 +127,8 @@ const setHtml = (html, text) => {
104
127
105
128
/**
106
129
* Sets a string on the Clipboard object via react-native-web
107
- *
108
- * @param {String } text
109
130
*/
110
- const setString = ( text ) => {
131
+ const setString : SetString = ( text ) => {
111
132
Clipboard . setString ( text ) ;
112
133
} ;
113
134
0 commit comments