mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-30 02:19:52 +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>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
@@ -14,6 +14,7 @@ import findClosestNode from '../utils/find-closest-node'
|
|||||||
import findDeepestNode from '../utils/find-deepest-node'
|
import findDeepestNode from '../utils/find-deepest-node'
|
||||||
import getPoint from '../utils/get-point'
|
import getPoint from '../utils/get-point'
|
||||||
import getTransferData from '../utils/get-transfer-data'
|
import getTransferData from '../utils/get-transfer-data'
|
||||||
|
import setTransferData from '../utils/set-transfer-data'
|
||||||
import { IS_FIREFOX, IS_MAC } from '../constants/environment'
|
import { IS_FIREFOX, IS_MAC } from '../constants/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,13 +445,7 @@ class Content extends React.Component {
|
|||||||
onDragOver = (event) => {
|
onDragOver = (event) => {
|
||||||
if (!this.isInEditor(event.target)) return
|
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
|
if (this.tmp.isDragging) return
|
||||||
this.tmp.isDragging = true
|
this.tmp.isDragging = true
|
||||||
@@ -479,7 +474,8 @@ class Content extends React.Component {
|
|||||||
const { state } = this.props
|
const { state } = this.props
|
||||||
const { fragment } = state
|
const { fragment } = state
|
||||||
const encoded = Base64.serializeNode(fragment)
|
const encoded = Base64.serializeNode(fragment)
|
||||||
dataTransfer.setData(TYPES.FRAGMENT, encoded)
|
|
||||||
|
setTransferData(dataTransfer, TYPES.FRAGMENT, encoded)
|
||||||
|
|
||||||
debug('onDragStart', { event })
|
debug('onDragStart', { event })
|
||||||
}
|
}
|
||||||
@@ -530,7 +526,14 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
// Add drop-specific information to the data.
|
// Add drop-specific information to the data.
|
||||||
data.target = target
|
data.target = target
|
||||||
|
|
||||||
|
// COMPAT: Edge throws "Permission denied" errors when
|
||||||
|
// accessing `dropEffect` or `effectAllowed` (2017/7/12)
|
||||||
|
try {
|
||||||
data.effect = dataTransfer.dropEffect
|
data.effect = dataTransfer.dropEffect
|
||||||
|
} catch (err) {
|
||||||
|
data.effect = null
|
||||||
|
}
|
||||||
|
|
||||||
if (data.type == 'fragment' || data.type == 'node') {
|
if (data.type == 'fragment' || data.type == 'node') {
|
||||||
data.isInternal = this.tmp.isInternalDrag
|
data.isInternal = this.tmp.isInternalDrag
|
||||||
|
@@ -10,6 +10,7 @@ import Leaf from './leaf'
|
|||||||
import Void from './void'
|
import Void from './void'
|
||||||
import getWindow from 'get-window'
|
import getWindow from 'get-window'
|
||||||
import scrollToSelection from '../utils/scroll-to-selection'
|
import scrollToSelection from '../utils/scroll-to-selection'
|
||||||
|
import setTransferData from '../utils/set-transfer-data'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug.
|
* Debug.
|
||||||
@@ -218,8 +219,9 @@ class Node extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const encoded = Base64.serializeNode(node, { preserveKeys: true })
|
const encoded = Base64.serializeNode(node, { preserveKeys: true })
|
||||||
const data = e.nativeEvent.dataTransfer
|
const { dataTransfer } = e.nativeEvent
|
||||||
data.setData(TYPES.NODE, encoded)
|
|
||||||
|
setTransferData(dataTransfer, TYPES.NODE, encoded)
|
||||||
|
|
||||||
this.debug('onDragStart', e)
|
this.debug('onDragStart', e)
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ function getTransferData(transfer) {
|
|||||||
let node = transfer.getData(TYPES.NODE) || null
|
let node = transfer.getData(TYPES.NODE) || null
|
||||||
const html = transfer.getData('text/html') || null
|
const html = transfer.getData('text/html') || null
|
||||||
const rich = transfer.getData('text/rtf') || null
|
const rich = transfer.getData('text/rtf') || null
|
||||||
const text = transfer.getData('text/plain') || null
|
let text = transfer.getData('text/plain') || null
|
||||||
let files
|
let files
|
||||||
|
|
||||||
// If there isn't a fragment, but there is HTML, check to see if the HTML is
|
// If there isn't a fragment, but there is HTML, check to see if the HTML is
|
||||||
@@ -37,10 +37,23 @@ function getTransferData(transfer) {
|
|||||||
if (encoded) fragment = encoded
|
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.
|
// Decode a fragment or node if they exist.
|
||||||
if (fragment) fragment = Base64.deserializeNode(fragment)
|
if (fragment) fragment = Base64.deserializeNode(fragment)
|
||||||
if (node) node = Base64.deserializeNode(node)
|
if (node) node = Base64.deserializeNode(node)
|
||||||
|
|
||||||
|
// COMPAT: Edge sometimes throws 'NotSupportedError'
|
||||||
|
// when accessing `transfer.items` (2017/7/12)
|
||||||
|
try {
|
||||||
// Get and normalize files if they exist.
|
// Get and normalize files if they exist.
|
||||||
if (transfer.items && transfer.items.length) {
|
if (transfer.items && transfer.items.length) {
|
||||||
files = Array.from(transfer.items)
|
files = Array.from(transfer.items)
|
||||||
@@ -49,6 +62,11 @@ function getTransferData(transfer) {
|
|||||||
} else if (transfer.files && transfer.files.length) {
|
} else if (transfer.files && transfer.files.length) {
|
||||||
files = Array.from(transfer.files)
|
files = Array.from(transfer.files)
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (transfer.files && transfer.files.length) {
|
||||||
|
files = Array.from(transfer.files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the type of the data.
|
// Determine the type of the data.
|
||||||
const data = { files, fragment, html, node, rich, text }
|
const data = { files, fragment, html, node, rich, text }
|
||||||
@@ -56,6 +74,30 @@ function getTransferData(transfer) {
|
|||||||
return data
|
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`.
|
* 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