1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-29 09:59:48 +02:00

Fix Edge Drag & Drop Issues (#923)

* Fix Edge doctype warning

* Remove getData() calls in onDragOver

Access to data within ondragover is prohibited

* Handle Edge errors accessing `dataTransfer.dropEffect`

* Work around Edge not supporting custom data in drag events

If unable to use `setData()` with custom type, then uses JSON obj to store data in 'text/plain'

* Fix more Edge errors

Edge sometimes throws 'NotSupportedError' whena accessing `items` property on `dataTransfer`

* Fix linting errors

* Fix formatting
This commit is contained in:
mjadobson
2017-07-12 16:43:38 +01:00
committed by Ian Storm Taylor
parent 17cfde67ce
commit 31c75590ae
5 changed files with 109 additions and 19 deletions

View File

@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />

View File

@@ -14,6 +14,7 @@ import findClosestNode from '../utils/find-closest-node'
import findDeepestNode from '../utils/find-deepest-node'
import getPoint from '../utils/get-point'
import getTransferData from '../utils/get-transfer-data'
import setTransferData from '../utils/set-transfer-data'
import { IS_FIREFOX, IS_MAC } from '../constants/environment'
/**
@@ -444,13 +445,7 @@ class Content extends React.Component {
onDragOver = (event) => {
if (!this.isInEditor(event.target)) return
const { dataTransfer } = event.nativeEvent
const data = getTransferData(dataTransfer)
// Prevent default when nodes are dragged to allow dropping.
if (data.type == 'node') {
event.preventDefault()
}
event.preventDefault()
if (this.tmp.isDragging) return
this.tmp.isDragging = true
@@ -479,7 +474,8 @@ class Content extends React.Component {
const { state } = this.props
const { fragment } = state
const encoded = Base64.serializeNode(fragment)
dataTransfer.setData(TYPES.FRAGMENT, encoded)
setTransferData(dataTransfer, TYPES.FRAGMENT, encoded)
debug('onDragStart', { event })
}
@@ -530,7 +526,14 @@ class Content extends React.Component {
// Add drop-specific information to the data.
data.target = target
data.effect = dataTransfer.dropEffect
// COMPAT: Edge throws "Permission denied" errors when
// accessing `dropEffect` or `effectAllowed` (2017/7/12)
try {
data.effect = dataTransfer.dropEffect
} catch (err) {
data.effect = null
}
if (data.type == 'fragment' || data.type == 'node') {
data.isInternal = this.tmp.isInternalDrag

View File

@@ -10,6 +10,7 @@ import Leaf from './leaf'
import Void from './void'
import getWindow from 'get-window'
import scrollToSelection from '../utils/scroll-to-selection'
import setTransferData from '../utils/set-transfer-data'
/**
* Debug.
@@ -218,8 +219,9 @@ class Node extends React.Component {
}
const encoded = Base64.serializeNode(node, { preserveKeys: true })
const data = e.nativeEvent.dataTransfer
data.setData(TYPES.NODE, encoded)
const { dataTransfer } = e.nativeEvent
setTransferData(dataTransfer, TYPES.NODE, encoded)
this.debug('onDragStart', e)
}

View File

@@ -22,7 +22,7 @@ function getTransferData(transfer) {
let node = transfer.getData(TYPES.NODE) || null
const html = transfer.getData('text/html') || null
const rich = transfer.getData('text/rtf') || null
const text = transfer.getData('text/plain') || null
let text = transfer.getData('text/plain') || null
let files
// If there isn't a fragment, but there is HTML, check to see if the HTML is
@@ -37,17 +37,35 @@ function getTransferData(transfer) {
if (encoded) fragment = encoded
}
// COMPAT: Edge doesn't handle custom data types
// These will be embedded in text/plain in this case (2017/7/12)
if (text) {
const embeddedTypes = getEmbeddedTypes(text)
if (embeddedTypes[TYPES.FRAGMENT]) fragment = embeddedTypes[TYPES.FRAGMENT]
if (embeddedTypes[TYPES.NODE]) node = embeddedTypes[TYPES.NODE]
if (embeddedTypes['text/plain']) text = embeddedTypes['text/plain']
}
// Decode a fragment or node if they exist.
if (fragment) fragment = Base64.deserializeNode(fragment)
if (node) node = Base64.deserializeNode(node)
// Get and normalize files if they exist.
if (transfer.items && transfer.items.length) {
files = Array.from(transfer.items)
.map(item => item.kind == 'file' ? item.getAsFile() : null)
.filter(exists => exists)
} else if (transfer.files && transfer.files.length) {
files = Array.from(transfer.files)
// COMPAT: Edge sometimes throws 'NotSupportedError'
// when accessing `transfer.items` (2017/7/12)
try {
// Get and normalize files if they exist.
if (transfer.items && transfer.items.length) {
files = Array.from(transfer.items)
.map(item => item.kind == 'file' ? item.getAsFile() : null)
.filter(exists => exists)
} else if (transfer.files && transfer.files.length) {
files = Array.from(transfer.files)
}
} catch (err) {
if (transfer.files && transfer.files.length) {
files = Array.from(transfer.files)
}
}
// Determine the type of the data.
@@ -56,6 +74,30 @@ function getTransferData(transfer) {
return data
}
/**
* Takes text input, checks whether contains embedded data
* and returns object with original text +/- additional data
*
* @param {String} text
* @return {Object}
*/
function getEmbeddedTypes(text) {
const prefix = 'SLATE-DATA-EMBED::'
if (text.substring(0, prefix.length) !== prefix) {
return { 'text/plain': text }
}
// Attempt to parse, if fails then just standard text/plain
// Otherwise, already had data embedded
try {
return JSON.parse(text.substring(prefix.length))
} catch (err) {
throw new Error('Unable to parse custom embedded drag data')
}
}
/**
* Get the type of a transfer from its `data`.
*

View File

@@ -0,0 +1,42 @@
/**
* Set data on dataTransfer
* COMPAT: In Edge, custom types throw errors, so embed all non-standard
* types in text/plain compound object. (2017/7/12)
*
* @param {DataTransfer} dataTransfer
* @param {String} type
* @param {String} content
*/
function setTransferData(dataTransfer, type, content) {
try {
dataTransfer.setData(type, content)
} catch (err) {
const prefix = 'SLATE-DATA-EMBED::'
let obj = {}
const text = dataTransfer.getData('text/plain')
// If prefixed, assume embedded drag data
if (text.substring(0, prefix.length) === prefix) {
try {
obj = JSON.parse(text.substring(prefix.length))
} catch (err2) {
throw new Error('Unable to parse custom embedded drag data')
}
} else {
obj['text/plain'] = text
}
obj[type] = content
dataTransfer.setData('text/plain', `${prefix}${JSON.stringify(obj)}`)
}
}
/**
* Export.
*
* @type {Function}
*/
export default setTransferData