Drag & drop for attaching files.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
// Drag & drop container: displays a banner on drag and calls a handler on drop.
|
||||
import React from 'react';
|
||||
|
||||
export default class DragAndDrop extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
// A file is being dragged over the container.
|
||||
dragging: false
|
||||
};
|
||||
this.dropRef = React.createRef();
|
||||
// Keeps track of the drag event.
|
||||
// Need a counter b/c the browser's 'drag' events may fire multiple times
|
||||
// when the user takes the mouse pointer over the container:
|
||||
// for the component itself and for all nested/child elements.
|
||||
this.dragCounter = 0;
|
||||
|
||||
this.handleDrag = this.handleDrag.bind(this);
|
||||
this.handleDragIn = this.handleDragIn.bind(this);
|
||||
this.handleDragOut = this.handleDragOut.bind(this);
|
||||
this.handleDrop = this.handleDrop.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let div = this.dropRef.current;
|
||||
div.addEventListener('dragenter', this.handleDragIn);
|
||||
div.addEventListener('dragleave', this.handleDragOut);
|
||||
div.addEventListener('dragover', this.handleDrag);
|
||||
div.addEventListener('drop', this.handleDrop);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
let div = this.dropRef.current;
|
||||
div.removeEventListener('dragenter', this.handleDragIn);
|
||||
div.removeEventListener('dragleave', this.handleDragOut);
|
||||
div.removeEventListener('dragover', this.handleDrag);
|
||||
div.removeEventListener('drop', this.handleDrop);
|
||||
}
|
||||
|
||||
handleDrag(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
handleDragIn(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dragCounter++;
|
||||
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
|
||||
this.setState({dragging: true});
|
||||
}
|
||||
}
|
||||
|
||||
handleDragOut(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dragCounter--;
|
||||
if (this.dragCounter == 0) {
|
||||
this.setState({dragging: false});
|
||||
}
|
||||
}
|
||||
|
||||
handleDrop(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.setState({dragging: false});
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||
this.props.onDrop(e.dataTransfer.files);
|
||||
e.dataTransfer.clearData();
|
||||
this.dragCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='drag-and-drop' ref={this.dropRef}>
|
||||
{this.state.dragging ?
|
||||
<div className='banner'>{this.props.actionPrompt}</div>
|
||||
: null}
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -7,6 +7,8 @@ import { Drafty } from 'tinode-sdk';
|
||||
// a dependency on webm-duration-fix.
|
||||
const AudioRecorder = React.lazy(_ => import('./audio-recorder.jsx'));
|
||||
|
||||
import DragAndDrop from './drag-and-drop.jsx';
|
||||
|
||||
import { KEYPRESS_DELAY } from '../config.js';
|
||||
import { filePasted } from '../lib/blob-helpers.js';
|
||||
import { replyFormatter } from '../lib/formatters.js';
|
||||
@@ -80,6 +82,7 @@ class SendMessage extends React.PureComponent {
|
||||
this.handleSend = this.handleSend.bind(this);
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
this.handleMessageTyping = this.handleMessageTyping.bind(this);
|
||||
this.handleDropAttach = this.handleDropAttach.bind(this);
|
||||
|
||||
this.handleQuoteClick = this.handleQuoteClick.bind(this);
|
||||
|
||||
@@ -159,6 +162,13 @@ class SendMessage extends React.PureComponent {
|
||||
e.target.value = '';
|
||||
}
|
||||
|
||||
handleDropAttach(files) {
|
||||
if (files && files.length > 0) {
|
||||
console.log('Dropping ', files);
|
||||
this.props.onAttachFile(files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
handleAttachAudio(url, preview, duration) {
|
||||
this.setState({audioRec: false});
|
||||
this.props.onAttachAudio(url, preview, duration);
|
||||
@@ -233,6 +243,13 @@ class SendMessage extends React.PureComponent {
|
||||
{this.state.quote}
|
||||
</div>) : null;
|
||||
const audioEnabled = this.state.audioAvailable && this.props.onAttachAudio;
|
||||
const inputField = (<textarea id="sendMessage" placeholder={prompt}
|
||||
value={this.state.message} onChange={this.handleMessageTyping}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
ref={(ref) => {this.messageEditArea = ref;}}
|
||||
autoFocus />);
|
||||
const inputArea = this.props.onAttachFile ?
|
||||
(<DragAndDrop actionPrompt={formatMessage(messages.icon_title_attach_file)} onDrop={this.handleDropAttach}>{inputField}</DragAndDrop>) : {inputField};
|
||||
return (
|
||||
<div id="send-message-wrapper">
|
||||
{!this.props.noInput ? quote : null}
|
||||
@@ -259,12 +276,7 @@ class SendMessage extends React.PureComponent {
|
||||
onDeleted={_ => this.setState({audioRec: false})}
|
||||
onFinished={this.handleAttachAudio}/>
|
||||
</Suspense>) :
|
||||
<textarea id="sendMessage" placeholder={prompt}
|
||||
value={this.state.message}
|
||||
onChange={this.handleMessageTyping}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
ref={ref => {this.messageEditArea = ref;}}
|
||||
autoFocus />)}
|
||||
inputArea)}
|
||||
{this.state.message || !audioEnabled ?
|
||||
<a href="#" onClick={this.handleSend} title={formatMessage(messages.icon_title_send)}>
|
||||
<i className="material-icons">{sendIcon}</i>
|
||||
|
||||
Reference in New Issue
Block a user