Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add a new drag and drop example
  • Loading branch information
Saadnajmi committed Oct 3, 2025
commit 42dc7693c5863a07a68f630023b3f623978cade0
214 changes: 214 additions & 0 deletions packages/rn-tester/js/examples/DragAndDrop/DragAndDropExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/

'use strict';

// [macOS

import type {PasteEvent} from 'react-native/Libraries/Components/TextInput/TextInput';

import ExampleTextInput from '../TextInput/ExampleTextInput';
import React from 'react';
import {Image, StyleSheet, Text, View} from 'react-native';

const styles = StyleSheet.create({
multiline: {
height: 50,
},
});

function OnDragEnterOnDragLeaveOnDrop(): React.Node {
// $FlowFixMe[missing-empty-array-annot]
const [log, setLog] = React.useState([]);
const appendLog = (line: string) => {
const limit = 6;
let newLog = log.slice(0, limit - 1);
newLog.unshift(line);
setLog(newLog);
};
return (
<>
<ExampleTextInput
multiline={false}
draggedTypes={'fileUrl'}
onDragEnter={e => appendLog('SinglelineEnter')}
onDragLeave={e => appendLog('SinglelineLeave')}
onDrop={e => appendLog('SinglelineDrop')}
style={styles.multiline}
placeholder="SINGLE LINE with onDragEnter|Leave() and onDrop()"
/>
<ExampleTextInput
multiline={true}
draggedTypes={'fileUrl'}
onDragEnter={e => appendLog('MultilineEnter')}
onDragLeave={e => appendLog('MultilineLeave')}
onDrop={e => appendLog('MultilineDrop')}
style={styles.multiline}
placeholder="MULTI LINE with onDragEnter|Leave() and onDrop()"
/>
<Text style={{height: 120}}>{log.join('\n')}</Text>
<ExampleTextInput
multiline={false}
style={styles.multiline}
placeholder="SINGLE LINE w/o onDragEnter|Leave() and onDrop()"
/>
<ExampleTextInput
multiline={true}
style={styles.multiline}
placeholder="MULTI LINE w/o onDragEnter|Leave() and onDrop()"
/>
</>
);
}

function OnPaste(): React.Node {
// $FlowFixMe[missing-empty-array-annot]
const [log, setLog] = React.useState([]);
const appendLog = (line: string) => {
const limit = 3;
let newLog = log.slice(0, limit - 1);
newLog.unshift(line);
setLog(newLog);
};
const [imageUri, setImageUri] = React.useState(
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==',
);
return (
<>
<ExampleTextInput
multiline={true}
style={styles.multiline}
onPaste={(e: PasteEvent) => {
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}}
pastedTypes={['string']}
placeholder="MULTI LINE with onPaste() text from clipboard"
/>
<ExampleTextInput
multiline={true}
style={styles.multiline}
onPaste={(e: PasteEvent) => {
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}}
pastedTypes={['fileUrl', 'image', 'string']}
placeholder="MULTI LINE with onPaste() for PNG/TIFF images from clipboard or fileUrl (via Finder) and text from clipboard"
/>
<Text style={{height: 30}}>{log.join('\n')}</Text>
<Image
source={{uri: imageUri}}
style={{
width: 128,
height: 128,
margin: 4,
borderWidth: 1,
borderColor: 'white',
}}
/>
</>
);
}

function OnDropView(): React.Node {
// $FlowFixMe[missing-empty-array-annot]
const [log, setLog] = React.useState([]);
const appendLog = (line: string) => {
const limit = 3;
let newLog = log.slice(0, limit - 1);
newLog.unshift(line);
setLog(newLog);
};
const [imageUri, setImageUri] = React.useState(
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==',
);
const [isDraggingOver, setIsDraggingOver] = React.useState(false);

return (
<>
<View
draggedTypes={['fileUrl', 'image']}
onDragEnter={e => {
appendLog('onDragEnter');
setIsDraggingOver(true);
}}
onDragLeave={e => {
appendLog('onDragLeave');
setIsDraggingOver(false);
}}
onDrop={e => {
appendLog('onDrop');
setIsDraggingOver(false);
if (e.nativeEvent.dataTransfer.files && e.nativeEvent.dataTransfer.files[0]) {
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}
}}
style={{
height: 150,
backgroundColor: isDraggingOver ? '#e3f2fd' : '#f0f0f0',
borderWidth: 2,
borderColor: isDraggingOver ? '#2196f3' : '#0066cc',
borderStyle: 'dashed',
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
marginVertical: 10,
}}>
<Text style={{color: isDraggingOver ? '#1976d2' : '#666', fontSize: 14}}>
{isDraggingOver ? 'Release to drop' : 'Drop an image or file here'}
</Text>
</View>
<View style={{flexDirection: 'row', gap: 10, alignItems: 'flex-start'}}>
<View style={{flex: 1}}>
<Text style={{fontWeight: 'bold', marginBottom: 4}}>Event Log:</Text>
<Text style={{height: 90}}>{log.join('\n')}</Text>
</View>
<View style={{flex: 1}}>
<Text style={{fontWeight: 'bold', marginBottom: 4}}>Dropped Image:</Text>
<Image
source={{uri: imageUri}}
style={{
width: 128,
height: 128,
borderWidth: 1,
borderColor: '#ccc',
}}
/>
</View>
</View>
</>
);
}

exports.title = 'Drag and Drop Events';
exports.category = 'UI';
exports.description = 'Demonstrates onDragEnter, onDragLeave, onDrop, and onPaste event handling in TextInput.';
exports.examples = [
{
title: 'onDrop with View - Drop Image',
render: function (): React.Node {
return <OnDropView />;
},
},
{
title: 'onDragEnter, onDragLeave and onDrop - Single- & MultiLineTextInput',
render: function (): React.Node {
return <OnDragEnterOnDragLeaveOnDrop />;
},
},
{
title: 'onPaste - MultiLineTextInput',
render: function (): React.Node {
return <OnPaste />;
},
},
];

// macOS]
108 changes: 0 additions & 108 deletions packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import type {
import type {KeyboardTypeOptions} from 'react-native/Libraries/Components/TextInput/TextInput';
// [macOS
import type {
PasteEvent,
SettingChangeEvent,
} from 'react-native/Libraries/Components/TextInput/TextInput'; // macOS]

Expand All @@ -29,7 +28,6 @@ import {
Alert,
Button,
InputAccessoryView,
Image, // [macOS]
Platform, // [macOS]
StyleSheet,
Switch,
Expand Down Expand Up @@ -388,99 +386,6 @@ function SpellingAndGrammarEvents(): React.Node {
</>
);
}

function OnDragEnterOnDragLeaveOnDrop(): React.Node {
// $FlowFixMe[missing-empty-array-annot]
const [log, setLog] = React.useState([]);
const appendLog = (line: string) => {
const limit = 6;
let newLog = log.slice(0, limit - 1);
newLog.unshift(line);
setLog(newLog);
};
return (
<>
<ExampleTextInput
multiline={false}
draggedTypes={'fileUrl'}
onDragEnter={e => appendLog('SinglelineEnter')}
onDragLeave={e => appendLog('SinglelineLeave')}
onDrop={e => appendLog('SinglelineDrop')}
style={styles.multiline}
placeholder="SINGLE LINE with onDragEnter|Leave() and onDrop()"
/>
<ExampleTextInput
multiline={true}
draggedTypes={'fileUrl'}
onDragEnter={e => appendLog('MultilineEnter')}
onDragLeave={e => appendLog('MultilineLeave')}
onDrop={e => appendLog('MultilineDrop')}
style={styles.multiline}
placeholder="MULTI LINE with onDragEnter|Leave() and onDrop()"
/>
<Text style={{height: 120}}>{log.join('\n')}</Text>
<ExampleTextInput
multiline={false}
style={styles.multiline}
placeholder="SINGLE LINE w/o onDragEnter|Leave() and onDrop()"
/>
<ExampleTextInput
multiline={true}
style={styles.multiline}
placeholder="MULTI LINE w/o onDragEnter|Leave() and onDrop()"
/>
</>
);
}

function OnPaste(): React.Node {
// $FlowFixMe[missing-empty-array-annot]
const [log, setLog] = React.useState([]);
const appendLog = (line: string) => {
const limit = 3;
let newLog = log.slice(0, limit - 1);
newLog.unshift(line);
setLog(newLog);
};
const [imageUri, setImageUri] = React.useState(
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==',
);
return (
<>
<ExampleTextInput
multiline={true}
style={styles.multiline}
onPaste={(e: PasteEvent) => {
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}}
pastedTypes={['string']}
placeholder="MULTI LINE with onPaste() text from clipboard"
/>
<ExampleTextInput
multiline={true}
style={styles.multiline}
onPaste={(e: PasteEvent) => {
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}}
pastedTypes={['fileUrl', 'image', 'string']}
placeholder="MULTI LINE with onPaste() for PNG/TIFF images from clipboard or fileUrl (via Finder) and text from clipboard"
/>
<Text style={{height: 30}}>{log.join('\n')}</Text>
<Image
source={{uri: imageUri}}
style={{
width: 128,
height: 128,
margin: 4,
borderWidth: 1,
borderColor: 'white',
}}
/>
</>
);
}
// macOS]

const textInputExamples: Array<RNTesterModuleExample> = [
Expand Down Expand Up @@ -1233,19 +1138,6 @@ if (Platform.OS === 'macos') {
);
},
},
{
title:
'onDragEnter, onDragLeave and onDrop - Single- & MultiLineTextInput',
render: function (): React.Node {
return <OnDragEnterOnDragLeaveOnDrop />;
},
},
{
title: 'onPaste - MultiLineTextInput',
render: function (): React.Node {
return <OnPaste />;
},
},
{
title: 'Cursor color',
render: function (): React.Node {
Expand Down
5 changes: 5 additions & 0 deletions packages/rn-tester/js/utils/RNTesterList.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ const APIs: Array<RNTesterModuleInfo> = ([
module: require('../examples/DisplayContents/DisplayContentsExample')
.default,
},
{
key: 'DragAndDropExample',
category: 'UI',
module: require('../examples/DragAndDrop/DragAndDropExample'),
},
// Only show the link for the example if the API is available.
// $FlowExpectedError[cannot-resolve-name]
typeof IntersectionObserver === 'function'
Expand Down
Loading