Skip to content

Commit

Permalink
Merge pull request #3732 from parasharrajat/drag-N-drop
Browse files Browse the repository at this point in the history
Extended Drop area to report 🗒️  body for attachment
  • Loading branch information
ctkochan22 authored Jun 28, 2021
2 parents 5eef861 + 0e81b37 commit 461f670
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/CONST.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 59 additions & 12 deletions src/components/TextInputFocusable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import _ from 'underscore';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
import Growl from '../../libs/Growl';
import themeColors from '../../styles/themes/default';
import CONST from '../../CONST';

const propTypes = {
/** Maximum number of lines in the text input */
Expand Down Expand Up @@ -32,13 +33,16 @@ const propTypes = {
/** When the input has cleared whoever owns this input should know about it */
onClear: PropTypes.func,

/** Callback to fire when a file has been dragged into the text input */
/** Callback to fire when a file has being dragged over the text input & report body */
onDragOver: PropTypes.func,

/** Callback to fire when a file has been dragged into the text input & report body */
onDragEnter: PropTypes.func,

/** Callback to fire when the user is no longer dragging over the text input */
/** Callback to fire when the user is no longer dragging over the text input & report body */
onDragLeave: PropTypes.func,

/** Callback to fire when a file is dropped on the text input */
/** Callback to fire when a file is dropped on the text input & report body */
onDrop: PropTypes.func,

/** Whether or not this TextInput is disabled. */
Expand Down Expand Up @@ -69,12 +73,13 @@ const defaultProps = {
onClear: () => {},
style: null,
onDragEnter: () => {},
onDragOver: () => {},
onDragLeave: () => {},
onDrop: () => {},
isDisabled: false,
autoFocus: false,
forwardedRef: null,
onSelectionChange: () => { },
onSelectionChange: () => {},
selection: {
start: 0,
end: 0,
Expand Down Expand Up @@ -114,6 +119,7 @@ class TextInputFocusable extends React.Component {
end: initialValue.length,
};
this.saveSelection = this.saveSelection.bind(this);
this.dragNDropListener = this.dragNDropListener.bind(this);
}

componentDidMount() {
Expand All @@ -131,10 +137,11 @@ class TextInputFocusable extends React.Component {
// listeners here and unbind when the component unmounts
if (this.textInput) {
// Firefox will not allow dropping unless we call preventDefault on the dragover event
this.textInput.addEventListener('dragover', e => e.preventDefault());
this.textInput.addEventListener('dragenter', this.props.onDragEnter);
this.textInput.addEventListener('dragleave', this.props.onDragLeave);
this.textInput.addEventListener('drop', this.props.onDrop);
// We listen on document to extend the Drop area beyond Composer
document.addEventListener('dragover', this.dragNDropListener);
document.addEventListener('dragenter', this.dragNDropListener);
document.addEventListener('dragleave', this.dragNDropListener);
document.addEventListener('drop', this.dragNDropListener);
this.textInput.addEventListener('paste', this.checkForAttachment.bind(this));
}
}
Expand All @@ -158,10 +165,10 @@ class TextInputFocusable extends React.Component {

componentWillUnmount() {
if (this.textInput) {
this.textInput.addEventListener('dragover', e => e.preventDefault());
this.textInput.removeEventListener('dragenter', this.props.onDragEnter);
this.textInput.removeEventListener('dragleave', this.props.onDragLeave);
this.textInput.removeEventListener('drop', this.props.onDrop);
document.removeEventListener('dragover', this.dragNDropListener);
document.removeEventListener('dragenter', this.dragNDropListener);
document.removeEventListener('dragleave', this.dragNDropListener);
document.removeEventListener('drop', this.dragNDropListener);
this.textInput.removeEventListener('paste', this.checkForAttachment.bind(this));
}
}
Expand All @@ -182,6 +189,46 @@ class TextInputFocusable extends React.Component {
return newNumberOfLines;
}

/**
* Handles all types of drag-N-drop events on the composer
*
* @param {Object} e native Event
* @memberof TextInputFocusable
*/
dragNDropListener(e) {
let isOriginComposer = false;
const handler = () => {
switch (e.type) {
case 'dragover':
e.preventDefault();
this.props.onDragOver(e, isOriginComposer);
break;
case 'dragenter':
e.dataTransfer.dropEffect = 'copy';
this.props.onDragEnter(e, isOriginComposer);
break;
case 'dragleave':
this.props.onDragLeave(e, isOriginComposer);
break;
case 'drop':
this.props.onDrop(e, isOriginComposer);
break;
default: break;
}
};

// We first check if drop target is composer so that it can be highlighted
if (this.textInput.contains(e.target)) {
isOriginComposer = true;
handler();
return;
}

if (document.getElementById(CONST.REPORT.DROP_NATIVE_ID).contains(e.target)) {
handler();
}
}

/**
* Keeps track of user cursor position on the Composer
*
Expand Down
11 changes: 10 additions & 1 deletion src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,16 @@ class ReportActionCompose extends React.Component {
placeholderTextColor={themeColors.placeholderText}
onChangeText={this.updateComment}
onKeyPress={this.triggerHotkeyActions}
onDragEnter={() => this.setState({isDraggingOver: true})}
onDragEnter={(e, isOriginComposer) => {
if (isOriginComposer) {
this.setState({isDraggingOver: true});
}
}}
onDragOver={(e, isOriginComposer) => {
if (isOriginComposer) {
this.setState({isDraggingOver: true});
}
}}
onDragLeave={() => this.setState({isDraggingOver: false})}
onDrop={(e) => {
e.preventDefault();
Expand Down
3 changes: 2 additions & 1 deletion src/pages/home/report/ReportView.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import KeyboardSpacer from '../../../components/KeyboardSpacer';
import styles from '../../../styles/styles';
import SwipeableView from '../../../components/SwipeableView';
import ONYXKEYS from '../../../ONYXKEYS';
import CONST from '../../../CONST';

const propTypes = {
/** The ID of the report the selected report */
Expand All @@ -29,7 +30,7 @@ const defaultProps = {
};

const ReportView = ({reportID, session}) => (
<View key={reportID} style={[styles.flex1, styles.justifyContentEnd]}>
<View nativeID={CONST.REPORT.DROP_NATIVE_ID} key={reportID} style={[styles.flex1, styles.justifyContentEnd]}>
<ReportActionsView reportID={reportID} />

{session.shouldShowComposeInput && (
Expand Down

0 comments on commit 461f670

Please sign in to comment.