Skip to content

Commit

Permalink
Fix dragging text around in editor3 when media is enabled (#3081)
Browse files Browse the repository at this point in the history
* Propagate event if drop was not handled

* Avoid dropping items into an atomic block
  • Loading branch information
pablopunk authored Sep 3, 2019
1 parent bf5c7b3 commit b0e67d7
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 9 deletions.
21 changes: 17 additions & 4 deletions scripts/core/editor3/components/BaseUnstyledComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import {getValidMediaType, canDropMedia} from './Editor3Component';
import {moveBlock, dragDrop, embed} from '../actions/editor3';
import {getEmbedObject} from './embeds/EmbedInput';
import {htmlComesFromDraftjsEditor} from 'core/editor3/helpers/htmlComesFromDraftjsEditor';

const EDITOR_BLOCK_TYPE = 'superdesk/editor3-block';

Expand Down Expand Up @@ -35,14 +36,13 @@ class BaseUnstyledComponent extends React.Component<any, any> {
onDrop(event) {
this.setState({over: false});

event.preventDefault();
event.stopPropagation();

let handled = false;
const block = getEditorBlock(event);

if (typeof block === 'string' && block.length > 0) {
// existing media item dropped to another place
this.props.dispatch(moveBlock(block, this.getDropBlockKey(), this.dropInsertionMode));
handled = true;
return;
}

Expand All @@ -54,6 +54,7 @@ class BaseUnstyledComponent extends React.Component<any, any> {
if (canDropMedia(event, this.props.editorProps)
&& (mediaType === 'Files' || mediaType.includes('application/superdesk'))) {
this.props.dispatch(dragDrop(dataTransfer, mediaType, blockKey));
handled = true;
} else if (
typeof link === 'string'
&& link.startsWith('http')
Expand All @@ -63,11 +64,23 @@ class BaseUnstyledComponent extends React.Component<any, any> {
.then((oEmbed) => {
this.props.dispatch(embed(oEmbed, blockKey));
});
handled = true;
} else if (mediaType === 'text/html' && this.props.editorProps.editorFormat.includes('embed')) {
this.props.dispatch(embed(event.originalEvent.dataTransfer.getData(mediaType), blockKey));
const html = event.originalEvent.dataTransfer.getData(mediaType);
const comingFromDraftJS = htmlComesFromDraftjsEditor(html);

if (!comingFromDraftJS) {
this.props.dispatch(embed(event.originalEvent.dataTransfer.getData(mediaType), blockKey));
handled = true;
}
} else {
console.warn('unsupported media type on drop', mediaType);
}

if (handled) {
event.preventDefault();
event.stopPropagation();
}
}

onDragOver(event) {
Expand Down
33 changes: 29 additions & 4 deletions scripts/core/editor3/components/Editor3Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
getDefaultKeyBinding,
DefaultDraftBlockRenderMap,
KeyBindingUtil,
SelectionState,
DraftDragType,
DraftHandleValue,
} from 'draft-js';
import {getVisibleSelectionRect} from 'draft-js';

Expand Down Expand Up @@ -64,12 +67,17 @@ export function getValidMediaType(event) {
*/
export function canDropMedia(e, editorConfig) {
const {editorFormat, readOnly, singleLine} = editorConfig;
const supportsMedia = !readOnly && !singleLine && editorFormat.indexOf('media') !== -1;
const supportsMedia = !readOnly && !singleLine && editorFormat.includes('media');

if (!supportsMedia) {
return false;
}

const mediaType = getValidMediaType(e.originalEvent);
const dataTransfer = e.originalEvent.dataTransfer;
let isValidMedia = !!mediaType;

if (mediaType === 'Files' && dataTransfer.files) {
if (mediaType === 'Files' && dataTransfer.files.length > 0) {
// checks if files dropped from external folder are valid or not
const isValidFileType = Object.values(dataTransfer.files).every(
(file: File) => file.type.startsWith('audio/')
Expand All @@ -80,7 +88,7 @@ export function canDropMedia(e, editorConfig) {
}
}

return supportsMedia && isValidMedia;
return isValidMedia;
}

interface IProps {
Expand Down Expand Up @@ -144,6 +152,7 @@ export class Editor3Component extends React.Component<IProps> {
this.handleKeyCommand = this.handleKeyCommand.bind(this);
this.handleBeforeInput = this.handleBeforeInput.bind(this);
this.keyBindingFn = this.keyBindingFn.bind(this);
this.handleDropOnEditor = this.handleDropOnEditor.bind(this);
this.spellcheck = this.spellcheck.bind(this);
this.spellcheckCancelFn = noop;
}
Expand Down Expand Up @@ -203,6 +212,21 @@ export class Editor3Component extends React.Component<IProps> {
return !canDropMedia(e, this.props);
}

handleDropOnEditor(selection: SelectionState, dataTransfer: Object, isInternal: DraftDragType): DraftHandleValue {
if (isInternal) {
const {editorState} = this.props;
const targetBlockKey = selection.getStartKey();
const block = editorState.getCurrentContent().getBlockForKey(targetBlockKey);

if (block && block.getType() === 'atomic') {
// Avoid dragging internal text inside an atomic block.
// Draft will replace the block data with the text, which
// will break the block until page refresh
return 'handled';
}
}
}

keyBindingFn(e) {
const {keyCode, shiftKey} = e;

Expand Down Expand Up @@ -449,7 +473,7 @@ export class Editor3Component extends React.Component<IProps> {
'unstyled__block--invisibles': this.props.invisibles,
});

const mediaEnabled = this.props.editorFormat.indexOf('media') !== -1;
const mediaEnabled = this.props.editorFormat.includes('media');

const blockRenderMap = DefaultDraftBlockRenderMap.merge(Map(
mediaEnabled ? {
Expand Down Expand Up @@ -480,6 +504,7 @@ export class Editor3Component extends React.Component<IProps> {
/>
<div className="focus-screen" onMouseDown={this.focus}>
<Editor editorState={editorState}
handleDrop={this.handleDropOnEditor}
handleKeyCommand={this.handleKeyCommand}
keyBindingFn={this.keyBindingFn}
handleBeforeInput={this.handleBeforeInput}
Expand Down
2 changes: 1 addition & 1 deletion scripts/core/editor3/helpers/htmlComesFromDraftjsEditor.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const htmlComesFromDraftjsEditor = (html) => new DOMParser().parseFromString(html, 'text/html')
export const htmlComesFromDraftjsEditor = (html: string) => new DOMParser().parseFromString(html, 'text/html')
.body.querySelector('[data-offset-key]') != null;

0 comments on commit b0e67d7

Please sign in to comment.