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:
committed by
Ian Storm Taylor
parent
17cfde67ce
commit
31c75590ae
@@ -1,3 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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`.
|
||||
*
|
||||
|
42
src/utils/set-transfer-data.js
Normal file
42
src/utils/set-transfer-data.js
Normal 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
|
Reference in New Issue
Block a user