mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-25 16:20:49 +02:00
cleanup many things in ./utils (#1061)
This commit is contained in:
@@ -59,7 +59,7 @@ class RichText extends React.Component {
|
|||||||
|
|
||||||
state = {
|
state = {
|
||||||
state: Raw.deserialize(initialState, { terse: true })
|
state: Raw.deserialize(initialState, { terse: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current selection has a mark with `type` in it.
|
* Check if the current selection has a mark with `type` in it.
|
||||||
|
@@ -9,13 +9,14 @@ import TRANSFER_TYPES from '../constants/transfer-types'
|
|||||||
import Base64 from '../serializers/base-64'
|
import Base64 from '../serializers/base-64'
|
||||||
import Node from './node'
|
import Node from './node'
|
||||||
import Selection from '../models/selection'
|
import Selection from '../models/selection'
|
||||||
|
import SlateTypes from '../utils/prop-types'
|
||||||
import extendSelection from '../utils/extend-selection'
|
import extendSelection from '../utils/extend-selection'
|
||||||
import findClosestNode from '../utils/find-closest-node'
|
import findClosestNode from '../utils/find-closest-node'
|
||||||
import findDeepestNode from '../utils/find-deepest-node'
|
import findDeepestNode from '../utils/find-deepest-node'
|
||||||
|
import getHtmlFromNativePaste from '../utils/get-html-from-native-paste'
|
||||||
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 setTransferData from '../utils/set-transfer-data'
|
||||||
import getHtmlFromNativePaste from '../utils/get-html-from-native-paste'
|
|
||||||
import { IS_FIREFOX, IS_MAC, IS_IE } from '../constants/environment'
|
import { IS_FIREFOX, IS_MAC, IS_IE } from '../constants/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,12 +59,12 @@ class Content extends React.Component {
|
|||||||
onSelect: Types.func.isRequired,
|
onSelect: Types.func.isRequired,
|
||||||
readOnly: Types.bool.isRequired,
|
readOnly: Types.bool.isRequired,
|
||||||
role: Types.string,
|
role: Types.string,
|
||||||
schema: Types.object,
|
schema: SlateTypes.schema.isRequired,
|
||||||
spellCheck: Types.bool.isRequired,
|
spellCheck: Types.bool.isRequired,
|
||||||
state: Types.object.isRequired,
|
state: SlateTypes.state.isRequired,
|
||||||
style: Types.object,
|
style: Types.object,
|
||||||
tabIndex: Types.number,
|
tabIndex: Types.number,
|
||||||
tagName: Types.string
|
tagName: Types.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,7 +75,7 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
style: {},
|
style: {},
|
||||||
tagName: 'div'
|
tagName: 'div',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -6,7 +6,7 @@ import Types from 'prop-types'
|
|||||||
|
|
||||||
import Stack from '../models/stack'
|
import Stack from '../models/stack'
|
||||||
import State from '../models/state'
|
import State from '../models/state'
|
||||||
import SlatePropTypes from '../utils/prop-types'
|
import SlateTypes from '../utils/prop-types'
|
||||||
import noop from '../utils/noop'
|
import noop from '../utils/noop'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +81,7 @@ class Editor extends React.Component {
|
|||||||
role: Types.string,
|
role: Types.string,
|
||||||
schema: Types.object,
|
schema: Types.object,
|
||||||
spellCheck: Types.bool,
|
spellCheck: Types.bool,
|
||||||
state: SlatePropTypes.state.isRequired,
|
state: SlateTypes.state.isRequired,
|
||||||
style: Types.object,
|
style: Types.object,
|
||||||
tabIndex: Types.number,
|
tabIndex: Types.number,
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import ReactDOM from 'react-dom'
|
|||||||
import Types from 'prop-types'
|
import Types from 'prop-types'
|
||||||
|
|
||||||
import OffsetKey from '../utils/offset-key'
|
import OffsetKey from '../utils/offset-key'
|
||||||
|
import SlateTypes from '../utils/prop-types'
|
||||||
import findDeepestNode from '../utils/find-deepest-node'
|
import findDeepestNode from '../utils/find-deepest-node'
|
||||||
import { IS_FIREFOX } from '../constants/environment'
|
import { IS_FIREFOX } from '../constants/environment'
|
||||||
|
|
||||||
@@ -31,17 +32,17 @@ class Leaf extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
block: Types.object.isRequired,
|
block: SlateTypes.block.isRequired,
|
||||||
editor: Types.object.isRequired,
|
editor: Types.object.isRequired,
|
||||||
index: Types.number.isRequired,
|
index: Types.number.isRequired,
|
||||||
marks: Types.object.isRequired,
|
marks: SlateTypes.marks.isRequired,
|
||||||
node: Types.object.isRequired,
|
node: SlateTypes.node.isRequired,
|
||||||
offset: Types.number.isRequired,
|
offset: Types.number.isRequired,
|
||||||
parent: Types.object.isRequired,
|
parent: SlateTypes.node.isRequired,
|
||||||
ranges: Types.object.isRequired,
|
ranges: SlateTypes.ranges.isRequired,
|
||||||
schema: Types.object.isRequired,
|
schema: SlateTypes.schema.isRequired,
|
||||||
state: Types.object.isRequired,
|
state: SlateTypes.state.isRequired,
|
||||||
text: Types.string.isRequired
|
text: Types.string.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -7,6 +7,7 @@ import Types from 'prop-types'
|
|||||||
import TRANSFER_TYPES from '../constants/transfer-types'
|
import TRANSFER_TYPES from '../constants/transfer-types'
|
||||||
import Base64 from '../serializers/base-64'
|
import Base64 from '../serializers/base-64'
|
||||||
import Leaf from './leaf'
|
import Leaf from './leaf'
|
||||||
|
import SlateTypes from '../utils/prop-types'
|
||||||
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'
|
||||||
@@ -35,13 +36,13 @@ class Node extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
block: Types.object,
|
block: SlateTypes.block,
|
||||||
editor: Types.object.isRequired,
|
editor: Types.object.isRequired,
|
||||||
node: Types.object.isRequired,
|
node: SlateTypes.node.isRequired,
|
||||||
parent: Types.object.isRequired,
|
parent: SlateTypes.node.isRequired,
|
||||||
readOnly: Types.bool.isRequired,
|
readOnly: Types.bool.isRequired,
|
||||||
schema: Types.object.isRequired,
|
schema: SlateTypes.schema.isRequired,
|
||||||
state: Types.object.isRequired
|
state: SlateTypes.state.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Types from 'prop-types'
|
import Types from 'prop-types'
|
||||||
|
|
||||||
|
import SlateTypes from '../utils/prop-types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placeholder.
|
* Placeholder.
|
||||||
*
|
*
|
||||||
@@ -20,10 +22,10 @@ class Placeholder extends React.Component {
|
|||||||
children: Types.any.isRequired,
|
children: Types.any.isRequired,
|
||||||
className: Types.string,
|
className: Types.string,
|
||||||
firstOnly: Types.bool,
|
firstOnly: Types.bool,
|
||||||
node: Types.object.isRequired,
|
node: SlateTypes.node.isRequired,
|
||||||
parent: Types.object,
|
parent: SlateTypes.node,
|
||||||
state: Types.object.isRequired,
|
state: SlateTypes.state.isRequired,
|
||||||
style: Types.object
|
style: Types.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +35,7 @@ class Placeholder extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
firstOnly: true
|
firstOnly: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -6,6 +6,7 @@ import Types from 'prop-types'
|
|||||||
import Leaf from './leaf'
|
import Leaf from './leaf'
|
||||||
import Mark from '../models/mark'
|
import Mark from '../models/mark'
|
||||||
import OffsetKey from '../utils/offset-key'
|
import OffsetKey from '../utils/offset-key'
|
||||||
|
import SlateTypes from '../utils/prop-types'
|
||||||
import { IS_FIREFOX } from '../constants/environment'
|
import { IS_FIREFOX } from '../constants/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,14 +32,14 @@ class Void extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
block: Types.object,
|
block: SlateTypes.block,
|
||||||
children: Types.any.isRequired,
|
children: Types.any.isRequired,
|
||||||
editor: Types.object.isRequired,
|
editor: Types.object.isRequired,
|
||||||
node: Types.object.isRequired,
|
node: SlateTypes.node.isRequired,
|
||||||
parent: Types.object.isRequired,
|
parent: SlateTypes.node.isRequired,
|
||||||
readOnly: Types.bool.isRequired,
|
readOnly: Types.bool.isRequired,
|
||||||
schema: Types.object.isRequired,
|
schema: SlateTypes.schema.isRequired,
|
||||||
state: Types.object.isRequired,
|
state: SlateTypes.state.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +50,7 @@ class Void extends React.Component {
|
|||||||
|
|
||||||
state = {
|
state = {
|
||||||
dragCounter: 0,
|
dragCounter: 0,
|
||||||
editable: false
|
editable: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -108,6 +108,17 @@ class Block extends Record(DEFAULTS) {
|
|||||||
return !!(value && value[MODEL_TYPES.BLOCK])
|
return !!(value && value[MODEL_TYPES.BLOCK])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a block list.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isBlockList(value) {
|
||||||
|
return List.isList(value) && value.size > 0 && Block.isBlock(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the node's kind.
|
* Get the node's kind.
|
||||||
*
|
*
|
||||||
|
@@ -85,6 +85,17 @@ class Character extends Record(DEFAULTS) {
|
|||||||
return !!(value && value[MODEL_TYPES.CHARACTER])
|
return !!(value && value[MODEL_TYPES.CHARACTER])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a character list.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isCharacterList(value) {
|
||||||
|
return List.isList(value) && value.size > 0 && Character.isCharacter(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deprecated.
|
* Deprecated.
|
||||||
*/
|
*/
|
||||||
|
@@ -108,6 +108,17 @@ class Inline extends Record(DEFAULTS) {
|
|||||||
return !!(value && value[MODEL_TYPES.INLINE])
|
return !!(value && value[MODEL_TYPES.INLINE])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a list of inlines.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isInlineList(value) {
|
||||||
|
return List.isList(value) && value.size > 0 && Inline.isInline(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the node's kind.
|
* Get the node's kind.
|
||||||
*
|
*
|
||||||
|
@@ -118,6 +118,17 @@ class Mark extends Record(DEFAULTS) {
|
|||||||
return !!(value && value[MODEL_TYPES.MARK])
|
return !!(value && value[MODEL_TYPES.MARK])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a set of marks.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isMarkSet(value) {
|
||||||
|
return Set.isSet(value) && value.size > 0 && Mark.isMark(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the kind.
|
* Get the kind.
|
||||||
*/
|
*/
|
||||||
|
@@ -113,6 +113,17 @@ class Node {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a list of nodes.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isNodeList(value) {
|
||||||
|
return List.isList(value) && value.size > 0 && Node.isNode(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the node has both descendants in that order, false otherwise. The
|
* True if the node has both descendants in that order, false otherwise. The
|
||||||
* order is depth-first, post-order.
|
* order is depth-first, post-order.
|
||||||
|
@@ -3,7 +3,7 @@ import MODEL_TYPES from '../constants/model-types'
|
|||||||
import Character from './character'
|
import Character from './character'
|
||||||
import Mark from './mark'
|
import Mark from './mark'
|
||||||
import isPlainObject from 'is-plain-object'
|
import isPlainObject from 'is-plain-object'
|
||||||
import { Record, Set } from 'immutable'
|
import { List, Record, Set } from 'immutable'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default properties.
|
* Default properties.
|
||||||
@@ -64,6 +64,17 @@ class Range extends Record(DEFAULTS) {
|
|||||||
return !!(value && value[MODEL_TYPES.RANGE])
|
return !!(value && value[MODEL_TYPES.RANGE])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a list of ranges.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isRangeList(value) {
|
||||||
|
return List.isList(value) && value.size > 0 && Range.isRange(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the node's kind.
|
* Get the node's kind.
|
||||||
*
|
*
|
||||||
|
@@ -94,6 +94,17 @@ class Text extends Record(DEFAULTS) {
|
|||||||
return !!(value && value[MODEL_TYPES.TEXT])
|
return !!(value && value[MODEL_TYPES.TEXT])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a `value` is a list of texts.
|
||||||
|
*
|
||||||
|
* @param {Any} value
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static isTextList(value) {
|
||||||
|
return List.isList(value) && value.size > 0 && Text.isText(value.first())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deprecated.
|
* Deprecated.
|
||||||
*/
|
*/
|
||||||
|
@@ -1,41 +1,49 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends the given selection to a given node and offset
|
* Extends a DOM `selection` to a given `el` and `offset`.
|
||||||
*
|
*
|
||||||
* @param {Selection} selection Selection instance
|
* COMPAT: In IE11, `selection.extend` doesn't exist natively, so we have to
|
||||||
* @param {Element} el Node to extend to
|
* polyfill it with this. (2017/09/06)
|
||||||
* @param {Number} offset Text offset to extend to
|
*
|
||||||
* @returns {Selection} Mutated Selection instance
|
* https://gist.github.com/tyler-johnson/0a3e8818de3f115b2a2dc47468ac0099
|
||||||
|
*
|
||||||
|
* @param {Selection} selection
|
||||||
|
* @param {Element} el
|
||||||
|
* @param {Number} offset
|
||||||
|
* @return {Selection}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function extendSelection(selection, el, offset) {
|
function extendSelection(selection, el, offset) {
|
||||||
// Use native method when possible
|
// Use native method whenever possible.
|
||||||
if (typeof selection.extend === 'function') return selection.extend(el, offset)
|
if (typeof selection.extend === 'function') {
|
||||||
|
return selection.extend(el, offset)
|
||||||
|
}
|
||||||
|
|
||||||
// See https://gist.github.com/tyler-johnson/0a3e8818de3f115b2a2dc47468ac0099
|
|
||||||
const range = document.createRange()
|
const range = document.createRange()
|
||||||
const anchor = document.createRange()
|
const anchor = document.createRange()
|
||||||
anchor.setStart(selection.anchorNode, selection.anchorOffset)
|
|
||||||
|
|
||||||
const focus = document.createRange()
|
const focus = document.createRange()
|
||||||
|
anchor.setStart(selection.anchorNode, selection.anchorOffset)
|
||||||
focus.setStart(el, offset)
|
focus.setStart(el, offset)
|
||||||
|
|
||||||
const v = focus.compareBoundaryPoints(Range.START_TO_START, anchor)
|
const v = focus.compareBoundaryPoints(Range.START_TO_START, anchor)
|
||||||
if (v >= 0) { // Focus is after anchor
|
|
||||||
|
// If the focus is after the anchor...
|
||||||
|
if (v >= 0) {
|
||||||
range.setStart(selection.anchorNode, selection.anchorOffset)
|
range.setStart(selection.anchorNode, selection.anchorOffset)
|
||||||
range.setEnd(el, offset)
|
range.setEnd(el, offset)
|
||||||
} else { // Anchor is after focus
|
}
|
||||||
|
|
||||||
|
// Otherwise, if the anchor if after the focus...
|
||||||
|
else {
|
||||||
range.setStart(el, offset)
|
range.setStart(el, offset)
|
||||||
range.setEnd(selection.anchorNode, selection.anchorOffset)
|
range.setEnd(selection.anchorNode, selection.anchorOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
selection.removeAllRanges()
|
selection.removeAllRanges()
|
||||||
selection.addRange(range)
|
selection.addRange(range)
|
||||||
|
|
||||||
return selection
|
return selection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
*
|
*
|
||||||
|
@@ -2,6 +2,11 @@
|
|||||||
/**
|
/**
|
||||||
* Find the closest ancestor of a DOM `element` that matches a given selector.
|
* Find the closest ancestor of a DOM `element` that matches a given selector.
|
||||||
*
|
*
|
||||||
|
* COMPAT: In IE11, the `Node.closest` method doesn't exist natively, so we
|
||||||
|
* have to polyfill it. (2017/09/06)
|
||||||
|
*
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
|
||||||
|
*
|
||||||
* @param {Element} node
|
* @param {Element} node
|
||||||
* @param {String} selector
|
* @param {String} selector
|
||||||
* @return {Element}
|
* @return {Element}
|
||||||
@@ -10,10 +15,10 @@
|
|||||||
function findClosestNode(node, selector) {
|
function findClosestNode(node, selector) {
|
||||||
if (typeof node.closest === 'function') return node.closest(selector)
|
if (typeof node.closest === 'function') return node.closest(selector)
|
||||||
|
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
|
|
||||||
const matches = (node.document || node.ownerDocument).querySelectorAll(selector)
|
const matches = (node.document || node.ownerDocument).querySelectorAll(selector)
|
||||||
let i
|
|
||||||
let parentNode = node
|
let parentNode = node
|
||||||
|
let i
|
||||||
|
|
||||||
do {
|
do {
|
||||||
i = matches.length
|
i = matches.length
|
||||||
while (--i >= 0 && matches.item(i) !== parentNode);
|
while (--i >= 0 && matches.item(i) !== parentNode);
|
||||||
|
@@ -10,9 +10,7 @@ function findDOMNode(node) {
|
|||||||
const el = window.document.querySelector(`[data-key="${node.key}"]`)
|
const el = window.document.querySelector(`[data-key="${node.key}"]`)
|
||||||
|
|
||||||
if (!el) {
|
if (!el) {
|
||||||
throw new Error(`Unable to find a DOM node for "${node.key}". This is
|
throw new Error(`Unable to find a DOM node for "${node.key}". This is often because of forgetting to add \`props.attributes\` to a component returned from \`renderNode\`.`)
|
||||||
often because of forgetting to add \`props.attributes\` to a component
|
|
||||||
returned from \`renderNode\`.`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return el
|
return el
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import { findDOMNode } from 'react-dom'
|
import { findDOMNode } from 'react-dom'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,28 +8,27 @@ import { findDOMNode } from 'react-dom'
|
|||||||
* is needed to return the HTML content. This solution was adapted from
|
* is needed to return the HTML content. This solution was adapted from
|
||||||
* http://stackoverflow.com/a/6804718.
|
* http://stackoverflow.com/a/6804718.
|
||||||
*
|
*
|
||||||
* @param {React.Component} component
|
* @param {Component} component
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getHtmlFromNativePaste(component, callback) {
|
function getHtmlFromNativePaste(component, callback) {
|
||||||
const el = findDOMNode(component)
|
const el = findDOMNode(component)
|
||||||
|
|
||||||
// Clone contentedible element, move out of screen and set focus.
|
// Create an off-screen clone of the element and give it focus.
|
||||||
const clone = el.cloneNode()
|
const clone = el.cloneNode()
|
||||||
clone.setAttribute('class', '')
|
clone.setAttribute('class', '')
|
||||||
clone.setAttribute('style', 'position: fixed; left: -9999px')
|
clone.setAttribute('style', 'position: fixed; left: -9999px')
|
||||||
el.parentNode.insertBefore(clone, el)
|
el.parentNode.insertBefore(clone, el)
|
||||||
clone.focus()
|
clone.focus()
|
||||||
|
|
||||||
// Clear call stack to let native paste behaviour occur on cloned element,
|
// Tick forward so the native paste behaviour occurs in cloned element and we
|
||||||
// then get what was pasted from the DOM and remove cloned element.
|
// can get what was pasted from the DOM.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (clone.childElementCount > 0) {
|
if (clone.childElementCount > 0) {
|
||||||
// If the node contains any child nodes, that is the HTML content.
|
// If the node contains any child nodes, that is the HTML content.
|
||||||
const html = clone.innerHTML
|
const html = clone.innerHTML
|
||||||
clone.parentNode.removeChild(clone)
|
clone.parentNode.removeChild(clone)
|
||||||
|
|
||||||
callback(html)
|
callback(html)
|
||||||
} else {
|
} else {
|
||||||
// Only plain text, no HTML.
|
// Only plain text, no HTML.
|
||||||
|
@@ -1,172 +1,84 @@
|
|||||||
|
|
||||||
|
import { Set } from 'immutable'
|
||||||
|
|
||||||
import Block from '../models/block'
|
import Block from '../models/block'
|
||||||
|
import Change from '../models/change'
|
||||||
import Character from '../models/character'
|
import Character from '../models/character'
|
||||||
|
import Data from '../models/data'
|
||||||
import Document from '../models/document'
|
import Document from '../models/document'
|
||||||
|
import History from '../models/history'
|
||||||
import Inline from '../models/inline'
|
import Inline from '../models/inline'
|
||||||
import Mark from '../models/mark'
|
import Mark from '../models/mark'
|
||||||
|
import Node from '../models/node'
|
||||||
import Range from '../models/range'
|
import Range from '../models/range'
|
||||||
import Schema from '../models/schema'
|
import Schema from '../models/schema'
|
||||||
import Selection from '../models/selection'
|
import Selection from '../models/selection'
|
||||||
|
import Stack from '../models/stack'
|
||||||
import State from '../models/state'
|
import State from '../models/state'
|
||||||
import Text from '../models/text'
|
import Text from '../models/text'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HOC Function that takes in a predicate prop type function, and allows an isRequired chain
|
* Create a prop type checker for Slate objects with `name` and `validate`.
|
||||||
*
|
*
|
||||||
* @param {Function} predicate
|
* @param {String} name
|
||||||
|
* @param {Function} validate
|
||||||
* @return {Function}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function createChainablePropType(predicate) {
|
function create(name, validate) {
|
||||||
function propType(props, propName, componentName) {
|
function check(isRequired, props, propName, componentName, location) {
|
||||||
if (props[propName] == null) return
|
const value = props[propName]
|
||||||
|
if (value == null && !isRequired) return null
|
||||||
return predicate(props, propName, componentName)
|
if (value == null && isRequired) return new Error(`The ${location} \`${propName}\` is marked as required in \`${componentName}\`, but it was not supplied.`)
|
||||||
|
if (validate(value)) return null
|
||||||
|
return new Error(`Invalid ${location} \`${propName}\` supplied to \`${componentName}\`, expected a Slate \`${name}\` but received: ${value}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
propType.isRequired = (props, propName, componentName) => {
|
function propType(...args) {
|
||||||
if (props[propName] == null) return new Error(`Required prop \`${propName}\` was not specified in \`${componentName}\`.`)
|
return check(false, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
return predicate(props, propName, componentName)
|
propType.isRequired = function (...args) {
|
||||||
|
return check(true, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
return propType
|
return propType
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Block
|
* Prop type checkers.
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const block = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Block.isBlock(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Block`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Character
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const character = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Character.isCharacter(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Character`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Document
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const document = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Document.isDocument(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Document`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Inline
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const inline = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Inline.isInline(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Inline`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Mark
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const mark = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Mark.isMark(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Mark`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Range
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const range = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Range.isRange(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Range`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Schema
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const schema = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Schema.isSchema(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Schema`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Selection
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const selection = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Selection.isSelection(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Selection`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate State
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const state = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !State.isState(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate State ${props[propName]}`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptype that checks if a prop is a Slate Text
|
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const text = createChainablePropType(
|
|
||||||
(props, propName, componentName) => {
|
|
||||||
return !Text.isText(props[propName]) ? new Error(`${propName} in ${componentName} is not a Slate Text`) : null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exported Slate proptypes
|
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
const Types = {
|
||||||
block,
|
block: create('Block', v => Block.isBlock(v)),
|
||||||
character,
|
blocks: create('List<Block>', v => Block.isBlockList(v)),
|
||||||
document,
|
change: create('Change', v => Change.isChange(v)),
|
||||||
inline,
|
character: create('Character', v => Character.isCharacter(v)),
|
||||||
mark,
|
characters: create('List<Character>', v => Character.isCharacterList(v)),
|
||||||
range,
|
data: create('Data', v => Data.isData(v)),
|
||||||
schema,
|
document: create('Document', v => Document.isDocument(v)),
|
||||||
selection,
|
history: create('History', v => History.isHistory(v)),
|
||||||
state,
|
inline: create('Inline', v => Inline.isInline(v)),
|
||||||
text,
|
mark: create('Mark', v => Mark.isMark(v)),
|
||||||
|
marks: create('Set<Mark>', v => (Set.isSet(v) && v.size === 0) || Mark.isMarkSet(v)),
|
||||||
|
node: create('Node', v => Node.isNode(v)),
|
||||||
|
nodes: create('List<Node>', v => Node.isNodeList(v)),
|
||||||
|
range: create('Range', v => Range.isRange(v)),
|
||||||
|
ranges: create('List<Range>', v => Range.isRangeList(v)),
|
||||||
|
schema: create('Schema', v => Schema.isSchema(v)),
|
||||||
|
selection: create('Selection', v => Selection.isSelection(v)),
|
||||||
|
stack: create('Stack', v => Stack.isStack(v)),
|
||||||
|
state: create('State', v => State.isState(v)),
|
||||||
|
text: create('Text', v => Text.isText(v)),
|
||||||
|
texts: create('List<Text>', v => Text.isTextList(v)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default Types
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set data on dataTransfer
|
* Set data with `type` and `content` on a `dataTransfer` object.
|
||||||
|
*
|
||||||
* COMPAT: In Edge, custom types throw errors, so embed all non-standard
|
* COMPAT: In Edge, custom types throw errors, so embed all non-standard
|
||||||
* types in text/plain compound object. (2017/7/12)
|
* types in text/plain compound object. (2017/7/12)
|
||||||
*
|
*
|
||||||
@@ -13,23 +15,26 @@ function setTransferData(dataTransfer, type, content) {
|
|||||||
dataTransfer.setData(type, content)
|
dataTransfer.setData(type, content)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const prefix = 'SLATE-DATA-EMBED::'
|
const prefix = 'SLATE-DATA-EMBED::'
|
||||||
let obj = {}
|
|
||||||
const text = dataTransfer.getData('text/plain')
|
const text = dataTransfer.getData('text/plain')
|
||||||
|
let obj = {}
|
||||||
|
|
||||||
// If prefixed, assume embedded drag data
|
// If the existing plain text data is prefixed, it's Slate JSON data.
|
||||||
if (text.substring(0, prefix.length) === prefix) {
|
if (text.substring(0, prefix.length) === prefix) {
|
||||||
try {
|
try {
|
||||||
obj = JSON.parse(text.substring(prefix.length))
|
obj = JSON.parse(text.substring(prefix.length))
|
||||||
} catch (err2) {
|
} catch (e) {
|
||||||
throw new Error('Unable to parse custom embedded drag data')
|
throw new Error('Failed to parse Slate data from `DataTransfer` object.')
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// Otherwise, it's just set it as is.
|
||||||
|
else {
|
||||||
obj['text/plain'] = text
|
obj['text/plain'] = text
|
||||||
}
|
}
|
||||||
|
|
||||||
obj[type] = content
|
obj[type] = content
|
||||||
|
const string = `${prefix}${JSON.stringify(obj)}`
|
||||||
dataTransfer.setData('text/plain', `${prefix}${JSON.stringify(obj)}`)
|
dataTransfer.setData('text/plain', string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user