Skip to content

Commit 42dc769

Browse files
committed
Add a new drag and drop example
1 parent 9a6095c commit 42dc769

File tree

3 files changed

+219
-108
lines changed

3 files changed

+219
-108
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
11+
'use strict';
12+
13+
// [macOS
14+
15+
import type {PasteEvent} from 'react-native/Libraries/Components/TextInput/TextInput';
16+
17+
import ExampleTextInput from '../TextInput/ExampleTextInput';
18+
import React from 'react';
19+
import {Image, StyleSheet, Text, View} from 'react-native';
20+
21+
const styles = StyleSheet.create({
22+
multiline: {
23+
height: 50,
24+
},
25+
});
26+
27+
function OnDragEnterOnDragLeaveOnDrop(): React.Node {
28+
// $FlowFixMe[missing-empty-array-annot]
29+
const [log, setLog] = React.useState([]);
30+
const appendLog = (line: string) => {
31+
const limit = 6;
32+
let newLog = log.slice(0, limit - 1);
33+
newLog.unshift(line);
34+
setLog(newLog);
35+
};
36+
return (
37+
<>
38+
<ExampleTextInput
39+
multiline={false}
40+
draggedTypes={'fileUrl'}
41+
onDragEnter={e => appendLog('SinglelineEnter')}
42+
onDragLeave={e => appendLog('SinglelineLeave')}
43+
onDrop={e => appendLog('SinglelineDrop')}
44+
style={styles.multiline}
45+
placeholder="SINGLE LINE with onDragEnter|Leave() and onDrop()"
46+
/>
47+
<ExampleTextInput
48+
multiline={true}
49+
draggedTypes={'fileUrl'}
50+
onDragEnter={e => appendLog('MultilineEnter')}
51+
onDragLeave={e => appendLog('MultilineLeave')}
52+
onDrop={e => appendLog('MultilineDrop')}
53+
style={styles.multiline}
54+
placeholder="MULTI LINE with onDragEnter|Leave() and onDrop()"
55+
/>
56+
<Text style={{height: 120}}>{log.join('\n')}</Text>
57+
<ExampleTextInput
58+
multiline={false}
59+
style={styles.multiline}
60+
placeholder="SINGLE LINE w/o onDragEnter|Leave() and onDrop()"
61+
/>
62+
<ExampleTextInput
63+
multiline={true}
64+
style={styles.multiline}
65+
placeholder="MULTI LINE w/o onDragEnter|Leave() and onDrop()"
66+
/>
67+
</>
68+
);
69+
}
70+
71+
function OnPaste(): React.Node {
72+
// $FlowFixMe[missing-empty-array-annot]
73+
const [log, setLog] = React.useState([]);
74+
const appendLog = (line: string) => {
75+
const limit = 3;
76+
let newLog = log.slice(0, limit - 1);
77+
newLog.unshift(line);
78+
setLog(newLog);
79+
};
80+
const [imageUri, setImageUri] = React.useState(
81+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==',
82+
);
83+
return (
84+
<>
85+
<ExampleTextInput
86+
multiline={true}
87+
style={styles.multiline}
88+
onPaste={(e: PasteEvent) => {
89+
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
90+
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
91+
}}
92+
pastedTypes={['string']}
93+
placeholder="MULTI LINE with onPaste() text from clipboard"
94+
/>
95+
<ExampleTextInput
96+
multiline={true}
97+
style={styles.multiline}
98+
onPaste={(e: PasteEvent) => {
99+
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
100+
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
101+
}}
102+
pastedTypes={['fileUrl', 'image', 'string']}
103+
placeholder="MULTI LINE with onPaste() for PNG/TIFF images from clipboard or fileUrl (via Finder) and text from clipboard"
104+
/>
105+
<Text style={{height: 30}}>{log.join('\n')}</Text>
106+
<Image
107+
source={{uri: imageUri}}
108+
style={{
109+
width: 128,
110+
height: 128,
111+
margin: 4,
112+
borderWidth: 1,
113+
borderColor: 'white',
114+
}}
115+
/>
116+
</>
117+
);
118+
}
119+
120+
function OnDropView(): React.Node {
121+
// $FlowFixMe[missing-empty-array-annot]
122+
const [log, setLog] = React.useState([]);
123+
const appendLog = (line: string) => {
124+
const limit = 3;
125+
let newLog = log.slice(0, limit - 1);
126+
newLog.unshift(line);
127+
setLog(newLog);
128+
};
129+
const [imageUri, setImageUri] = React.useState(
130+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==',
131+
);
132+
const [isDraggingOver, setIsDraggingOver] = React.useState(false);
133+
134+
return (
135+
<>
136+
<View
137+
draggedTypes={['fileUrl', 'image']}
138+
onDragEnter={e => {
139+
appendLog('onDragEnter');
140+
setIsDraggingOver(true);
141+
}}
142+
onDragLeave={e => {
143+
appendLog('onDragLeave');
144+
setIsDraggingOver(false);
145+
}}
146+
onDrop={e => {
147+
appendLog('onDrop');
148+
setIsDraggingOver(false);
149+
if (e.nativeEvent.dataTransfer.files && e.nativeEvent.dataTransfer.files[0]) {
150+
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
151+
}
152+
}}
153+
style={{
154+
height: 150,
155+
backgroundColor: isDraggingOver ? '#e3f2fd' : '#f0f0f0',
156+
borderWidth: 2,
157+
borderColor: isDraggingOver ? '#2196f3' : '#0066cc',
158+
borderStyle: 'dashed',
159+
borderRadius: 8,
160+
justifyContent: 'center',
161+
alignItems: 'center',
162+
marginVertical: 10,
163+
}}>
164+
<Text style={{color: isDraggingOver ? '#1976d2' : '#666', fontSize: 14}}>
165+
{isDraggingOver ? 'Release to drop' : 'Drop an image or file here'}
166+
</Text>
167+
</View>
168+
<View style={{flexDirection: 'row', gap: 10, alignItems: 'flex-start'}}>
169+
<View style={{flex: 1}}>
170+
<Text style={{fontWeight: 'bold', marginBottom: 4}}>Event Log:</Text>
171+
<Text style={{height: 90}}>{log.join('\n')}</Text>
172+
</View>
173+
<View style={{flex: 1}}>
174+
<Text style={{fontWeight: 'bold', marginBottom: 4}}>Dropped Image:</Text>
175+
<Image
176+
source={{uri: imageUri}}
177+
style={{
178+
width: 128,
179+
height: 128,
180+
borderWidth: 1,
181+
borderColor: '#ccc',
182+
}}
183+
/>
184+
</View>
185+
</View>
186+
</>
187+
);
188+
}
189+
190+
exports.title = 'Drag and Drop Events';
191+
exports.category = 'UI';
192+
exports.description = 'Demonstrates onDragEnter, onDragLeave, onDrop, and onPaste event handling in TextInput.';
193+
exports.examples = [
194+
{
195+
title: 'onDrop with View - Drop Image',
196+
render: function (): React.Node {
197+
return <OnDropView />;
198+
},
199+
},
200+
{
201+
title: 'onDragEnter, onDragLeave and onDrop - Single- & MultiLineTextInput',
202+
render: function (): React.Node {
203+
return <OnDragEnterOnDragLeaveOnDrop />;
204+
},
205+
},
206+
{
207+
title: 'onPaste - MultiLineTextInput',
208+
render: function (): React.Node {
209+
return <OnPaste />;
210+
},
211+
},
212+
];
213+
214+
// macOS]

packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import type {
1717
import type {KeyboardTypeOptions} from 'react-native/Libraries/Components/TextInput/TextInput';
1818
// [macOS
1919
import type {
20-
PasteEvent,
2120
SettingChangeEvent,
2221
} from 'react-native/Libraries/Components/TextInput/TextInput'; // macOS]
2322

@@ -29,7 +28,6 @@ import {
2928
Alert,
3029
Button,
3130
InputAccessoryView,
32-
Image, // [macOS]
3331
Platform, // [macOS]
3432
StyleSheet,
3533
Switch,
@@ -388,99 +386,6 @@ function SpellingAndGrammarEvents(): React.Node {
388386
</>
389387
);
390388
}
391-
392-
function OnDragEnterOnDragLeaveOnDrop(): React.Node {
393-
// $FlowFixMe[missing-empty-array-annot]
394-
const [log, setLog] = React.useState([]);
395-
const appendLog = (line: string) => {
396-
const limit = 6;
397-
let newLog = log.slice(0, limit - 1);
398-
newLog.unshift(line);
399-
setLog(newLog);
400-
};
401-
return (
402-
<>
403-
<ExampleTextInput
404-
multiline={false}
405-
draggedTypes={'fileUrl'}
406-
onDragEnter={e => appendLog('SinglelineEnter')}
407-
onDragLeave={e => appendLog('SinglelineLeave')}
408-
onDrop={e => appendLog('SinglelineDrop')}
409-
style={styles.multiline}
410-
placeholder="SINGLE LINE with onDragEnter|Leave() and onDrop()"
411-
/>
412-
<ExampleTextInput
413-
multiline={true}
414-
draggedTypes={'fileUrl'}
415-
onDragEnter={e => appendLog('MultilineEnter')}
416-
onDragLeave={e => appendLog('MultilineLeave')}
417-
onDrop={e => appendLog('MultilineDrop')}
418-
style={styles.multiline}
419-
placeholder="MULTI LINE with onDragEnter|Leave() and onDrop()"
420-
/>
421-
<Text style={{height: 120}}>{log.join('\n')}</Text>
422-
<ExampleTextInput
423-
multiline={false}
424-
style={styles.multiline}
425-
placeholder="SINGLE LINE w/o onDragEnter|Leave() and onDrop()"
426-
/>
427-
<ExampleTextInput
428-
multiline={true}
429-
style={styles.multiline}
430-
placeholder="MULTI LINE w/o onDragEnter|Leave() and onDrop()"
431-
/>
432-
</>
433-
);
434-
}
435-
436-
function OnPaste(): React.Node {
437-
// $FlowFixMe[missing-empty-array-annot]
438-
const [log, setLog] = React.useState([]);
439-
const appendLog = (line: string) => {
440-
const limit = 3;
441-
let newLog = log.slice(0, limit - 1);
442-
newLog.unshift(line);
443-
setLog(newLog);
444-
};
445-
const [imageUri, setImageUri] = React.useState(
446-
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==',
447-
);
448-
return (
449-
<>
450-
<ExampleTextInput
451-
multiline={true}
452-
style={styles.multiline}
453-
onPaste={(e: PasteEvent) => {
454-
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
455-
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
456-
}}
457-
pastedTypes={['string']}
458-
placeholder="MULTI LINE with onPaste() text from clipboard"
459-
/>
460-
<ExampleTextInput
461-
multiline={true}
462-
style={styles.multiline}
463-
onPaste={(e: PasteEvent) => {
464-
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
465-
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
466-
}}
467-
pastedTypes={['fileUrl', 'image', 'string']}
468-
placeholder="MULTI LINE with onPaste() for PNG/TIFF images from clipboard or fileUrl (via Finder) and text from clipboard"
469-
/>
470-
<Text style={{height: 30}}>{log.join('\n')}</Text>
471-
<Image
472-
source={{uri: imageUri}}
473-
style={{
474-
width: 128,
475-
height: 128,
476-
margin: 4,
477-
borderWidth: 1,
478-
borderColor: 'white',
479-
}}
480-
/>
481-
</>
482-
);
483-
}
484389
// macOS]
485390

486391
const textInputExamples: Array<RNTesterModuleExample> = [
@@ -1233,19 +1138,6 @@ if (Platform.OS === 'macos') {
12331138
);
12341139
},
12351140
},
1236-
{
1237-
title:
1238-
'onDragEnter, onDragLeave and onDrop - Single- & MultiLineTextInput',
1239-
render: function (): React.Node {
1240-
return <OnDragEnterOnDragLeaveOnDrop />;
1241-
},
1242-
},
1243-
{
1244-
title: 'onPaste - MultiLineTextInput',
1245-
render: function (): React.Node {
1246-
return <OnPaste />;
1247-
},
1248-
},
12491141
{
12501142
title: 'Cursor color',
12511143
render: function (): React.Node {

packages/rn-tester/js/utils/RNTesterList.ios.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@ const APIs: Array<RNTesterModuleInfo> = ([
252252
module: require('../examples/DisplayContents/DisplayContentsExample')
253253
.default,
254254
},
255+
{
256+
key: 'DragAndDropExample',
257+
category: 'UI',
258+
module: require('../examples/DragAndDrop/DragAndDropExample'),
259+
},
255260
// Only show the link for the example if the API is available.
256261
// $FlowExpectedError[cannot-resolve-name]
257262
typeof IntersectionObserver === 'function'

0 commit comments

Comments
 (0)