mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-31 02:49:56 +02:00
Add Prettier with ESLint integration (#1589)
* Add Prettier, with basic config and ESLint integration * Apply Prettier to all files using `yarn lint --fix` * Tell Prettier to ignore an empty text in a test output. * Run Prettier on JS files not handled by ESLint, and lint them too
This commit is contained in:
committed by
Ian Storm Taylor
parent
f28c59a26e
commit
3339d088e1
@@ -1,4 +1,3 @@
|
||||
|
||||
import { Value } from 'slate'
|
||||
import { atob, btoa } from 'isomorphic-base64'
|
||||
|
||||
@@ -91,5 +90,5 @@ export default {
|
||||
deserialize,
|
||||
deserializeNode,
|
||||
serialize,
|
||||
serializeNode
|
||||
serializeNode,
|
||||
}
|
||||
|
@@ -6,11 +6,10 @@
|
||||
* @type {Boolean}
|
||||
*/
|
||||
|
||||
const IS_DEV = (
|
||||
const IS_DEV =
|
||||
typeof process !== 'undefined' &&
|
||||
process.env &&
|
||||
process.env.NODE_ENV !== 'production'
|
||||
)
|
||||
|
||||
/**
|
||||
* Has console?
|
||||
@@ -18,12 +17,11 @@ const IS_DEV = (
|
||||
* @type {Boolean}
|
||||
*/
|
||||
|
||||
const HAS_CONSOLE = (
|
||||
const HAS_CONSOLE =
|
||||
typeof console != 'undefined' &&
|
||||
typeof console.log == 'function' &&
|
||||
typeof console.warn == 'function' &&
|
||||
typeof console.error == 'function'
|
||||
)
|
||||
|
||||
/**
|
||||
* Log a `message` at `level`.
|
||||
@@ -50,7 +48,6 @@ function log(level, message, ...args) {
|
||||
* @param {Any} ...args
|
||||
*/
|
||||
|
||||
|
||||
function error(message, ...args) {
|
||||
if (HAS_CONSOLE) {
|
||||
console.error(message, ...args)
|
||||
|
@@ -13,23 +13,27 @@ const html = new Html({
|
||||
switch (obj.object) {
|
||||
case 'block': {
|
||||
switch (obj.type) {
|
||||
case 'paragraph': return React.createElement('p', {}, children)
|
||||
case 'quote': return React.createElement('blockquote', {}, children)
|
||||
case 'paragraph':
|
||||
return React.createElement('p', {}, children)
|
||||
case 'quote':
|
||||
return React.createElement('blockquote', {}, children)
|
||||
}
|
||||
}
|
||||
case 'mark': {
|
||||
switch (obj.type) {
|
||||
case 'bold': return React.createElement('strong', {}, children)
|
||||
case 'italic': return React.createElement('em', {}, children)
|
||||
case 'bold':
|
||||
return React.createElement('strong', {}, children)
|
||||
case 'italic':
|
||||
return React.createElement('em', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default function (string) {
|
||||
export default function(string) {
|
||||
html.deserialize(string)
|
||||
}
|
||||
|
||||
@@ -39,4 +43,6 @@ export const input = `
|
||||
This is editable <strong>rich</strong> text, <em>much</em> better than a textarea!
|
||||
</p>
|
||||
</blockquote>
|
||||
`.trim().repeat(10)
|
||||
`
|
||||
.trim()
|
||||
.repeat(10)
|
||||
|
@@ -14,23 +14,27 @@ const html = new Html({
|
||||
switch (obj.object) {
|
||||
case 'block': {
|
||||
switch (obj.type) {
|
||||
case 'paragraph': return React.createElement('p', {}, children)
|
||||
case 'quote': return React.createElement('blockquote', {}, children)
|
||||
case 'paragraph':
|
||||
return React.createElement('p', {}, children)
|
||||
case 'quote':
|
||||
return React.createElement('blockquote', {}, children)
|
||||
}
|
||||
}
|
||||
case 'mark': {
|
||||
switch (obj.type) {
|
||||
case 'bold': return React.createElement('strong', {}, children)
|
||||
case 'italic': return React.createElement('em', {}, children)
|
||||
case 'bold':
|
||||
return React.createElement('strong', {}, children)
|
||||
case 'italic':
|
||||
return React.createElement('em', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default function (state) {
|
||||
export default function(state) {
|
||||
html.serialize(state)
|
||||
}
|
||||
|
||||
@@ -40,7 +44,8 @@ export const input = (
|
||||
{Array.from(Array(10)).map(() => (
|
||||
<quote>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a textarea!
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</quote>
|
||||
))}
|
||||
|
@@ -9,9 +9,11 @@ import { __clear } from '../../slate/lib/utils/memoize'
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs.readdirSync(categoryDir).filter(c => c[0] != '.' && c != 'index.js')
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach((category) => {
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 50)
|
||||
set('mintime', 1000)
|
||||
@@ -23,9 +25,12 @@ categories.forEach((category) => {
|
||||
}
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs.readdirSync(benchmarkDir).filter(b => b[0] != '.' && !!~b.indexOf('.js')).map(b => basename(b, extname(b)))
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach((benchmark) => {
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import React from 'react'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import typeOf from 'type-of'
|
||||
@@ -13,7 +12,7 @@ import { Record } from 'immutable'
|
||||
|
||||
const String = new Record({
|
||||
object: 'string',
|
||||
text: ''
|
||||
text: '',
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -24,15 +23,16 @@ const String = new Record({
|
||||
*/
|
||||
|
||||
const TEXT_RULE = {
|
||||
|
||||
deserialize(el) {
|
||||
if (el.tagName && el.tagName.toLowerCase() === 'br') {
|
||||
return {
|
||||
object: 'text',
|
||||
leaves: [{
|
||||
object: 'leaf',
|
||||
text: '\n'
|
||||
}]
|
||||
leaves: [
|
||||
{
|
||||
object: 'leaf',
|
||||
text: '\n',
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,26 +41,25 @@ const TEXT_RULE = {
|
||||
|
||||
return {
|
||||
object: 'text',
|
||||
leaves: [{
|
||||
object: 'leaf',
|
||||
text: el.nodeValue
|
||||
}]
|
||||
leaves: [
|
||||
{
|
||||
object: 'leaf',
|
||||
text: el.nodeValue,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
serialize(obj, children) {
|
||||
if (obj.object === 'string') {
|
||||
return children
|
||||
.split('\n')
|
||||
.reduce((array, text, i) => {
|
||||
if (i != 0) array.push(<br />)
|
||||
array.push(text)
|
||||
return array
|
||||
}, [])
|
||||
return children.split('\n').reduce((array, text, i) => {
|
||||
if (i != 0) array.push(<br />)
|
||||
array.push(text)
|
||||
return array
|
||||
}, [])
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +71,9 @@ const TEXT_RULE = {
|
||||
|
||||
function defaultParseHtml(html) {
|
||||
if (typeof DOMParser === 'undefined') {
|
||||
throw new Error('The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.')
|
||||
throw new Error(
|
||||
'The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.'
|
||||
)
|
||||
}
|
||||
|
||||
const parsed = new DOMParser().parseFromString(html, 'text/html')
|
||||
@@ -87,7 +88,6 @@ function defaultParseHtml(html) {
|
||||
*/
|
||||
|
||||
class Html {
|
||||
|
||||
/**
|
||||
* Create a new serializer with `rules`.
|
||||
*
|
||||
@@ -106,7 +106,7 @@ class Html {
|
||||
|
||||
defaultBlock = Node.createProperties(defaultBlock)
|
||||
|
||||
this.rules = [ ...rules, TEXT_RULE ]
|
||||
this.rules = [...rules, TEXT_RULE]
|
||||
this.defaultBlock = defaultBlock
|
||||
this.parseHtml = parseHtml
|
||||
}
|
||||
@@ -154,24 +154,26 @@ class Html {
|
||||
|
||||
// TODO: pretty sure this is no longer needed.
|
||||
if (nodes.length == 0) {
|
||||
nodes = [{
|
||||
object: 'block',
|
||||
data: {},
|
||||
isVoid: false,
|
||||
...defaultBlock,
|
||||
nodes: [
|
||||
{
|
||||
object: 'text',
|
||||
leaves: [
|
||||
{
|
||||
object: 'leaf',
|
||||
text: '',
|
||||
marks: [],
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}]
|
||||
nodes = [
|
||||
{
|
||||
object: 'block',
|
||||
data: {},
|
||||
isVoid: false,
|
||||
...defaultBlock,
|
||||
nodes: [
|
||||
{
|
||||
object: 'text',
|
||||
leaves: [
|
||||
{
|
||||
object: 'leaf',
|
||||
text: '',
|
||||
marks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const json = {
|
||||
@@ -180,7 +182,7 @@ class Html {
|
||||
object: 'document',
|
||||
data: {},
|
||||
nodes,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const ret = toJSON ? json : Value.fromJSON(json)
|
||||
@@ -197,7 +199,7 @@ class Html {
|
||||
deserializeElements = (elements = []) => {
|
||||
let nodes = []
|
||||
|
||||
elements.filter(this.cruftNewline).forEach((element) => {
|
||||
elements.filter(this.cruftNewline).forEach(element => {
|
||||
const node = this.deserializeElement(element)
|
||||
switch (typeOf(node)) {
|
||||
case 'array':
|
||||
@@ -219,14 +221,14 @@ class Html {
|
||||
* @return {Any}
|
||||
*/
|
||||
|
||||
deserializeElement = (element) => {
|
||||
deserializeElement = element => {
|
||||
let node
|
||||
|
||||
if (!element.tagName) {
|
||||
element.tagName = ''
|
||||
}
|
||||
|
||||
const next = (elements) => {
|
||||
const next = elements => {
|
||||
if (Object.prototype.toString.call(elements) == '[object NodeList]') {
|
||||
elements = Array.from(elements)
|
||||
}
|
||||
@@ -240,7 +242,9 @@ class Html {
|
||||
case 'undefined':
|
||||
return
|
||||
default:
|
||||
throw new Error(`The \`next\` argument was called with invalid children: "${elements}".`)
|
||||
throw new Error(
|
||||
`The \`next\` argument was called with invalid children: "${elements}".`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,8 +253,15 @@ class Html {
|
||||
const ret = rule.deserialize(element, next)
|
||||
const type = typeOf(ret)
|
||||
|
||||
if (type != 'array' && type != 'object' && type != 'null' && type != 'undefined') {
|
||||
throw new Error(`A rule returned an invalid deserialized representation: "${node}".`)
|
||||
if (
|
||||
type != 'array' &&
|
||||
type != 'object' &&
|
||||
type != 'null' &&
|
||||
type != 'undefined'
|
||||
) {
|
||||
throw new Error(
|
||||
`A rule returned an invalid deserialized representation: "${node}".`
|
||||
)
|
||||
}
|
||||
|
||||
if (ret === undefined) {
|
||||
@@ -276,23 +287,19 @@ class Html {
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
deserializeMark = (mark) => {
|
||||
deserializeMark = mark => {
|
||||
const { type, data } = mark
|
||||
|
||||
const applyMark = (node) => {
|
||||
const applyMark = node => {
|
||||
if (node.object == 'mark') {
|
||||
return this.deserializeMark(node)
|
||||
}
|
||||
|
||||
else if (node.object == 'text') {
|
||||
node.leaves = node.leaves.map((leaf) => {
|
||||
} else if (node.object == 'text') {
|
||||
node.leaves = node.leaves.map(leaf => {
|
||||
leaf.marks = leaf.marks || []
|
||||
leaf.marks.push({ type, data })
|
||||
return leaf
|
||||
})
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
node.nodes = node.nodes.map(applyMark)
|
||||
}
|
||||
|
||||
@@ -333,7 +340,7 @@ class Html {
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
serializeNode = (node) => {
|
||||
serializeNode = node => {
|
||||
if (node.object === 'text') {
|
||||
const leaves = node.getLeaves()
|
||||
return leaves.map(this.serializeLeaf)
|
||||
@@ -357,7 +364,7 @@ class Html {
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
serializeLeaf = (leaf) => {
|
||||
serializeLeaf = leaf => {
|
||||
const string = new String({ text: leaf.text })
|
||||
const text = this.serializeString(string)
|
||||
|
||||
@@ -379,7 +386,7 @@ class Html {
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
serializeString = (string) => {
|
||||
serializeString = string => {
|
||||
for (const rule of this.rules) {
|
||||
if (!rule.serialize) continue
|
||||
const ret = rule.serialize(string, string.text)
|
||||
@@ -394,10 +401,9 @@ class Html {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
cruftNewline = (element) => {
|
||||
cruftNewline = element => {
|
||||
return !(element.nodeName === '#text' && element.nodeValue == '\n')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -23,9 +22,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -36,9 +35,7 @@ export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<quote>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</quote>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -16,9 +15,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -17,9 +16,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -29,9 +28,7 @@ export const input = `
|
||||
export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph thing="value">
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph thing="value">one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -16,9 +15,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -16,9 +15,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -28,9 +27,7 @@ export const input = `
|
||||
export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -16,15 +15,15 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultBlock: {
|
||||
type: 'default',
|
||||
data: {
|
||||
thing: 'value'
|
||||
}
|
||||
}
|
||||
thing: 'value',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -35,9 +34,7 @@ export const input = `
|
||||
export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<block type="default" data={{ thing: 'value' }}>
|
||||
two
|
||||
</block>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -16,9 +15,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -29,9 +28,7 @@ export const input = `
|
||||
export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -30,9 +29,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -44,9 +43,7 @@ export const output = (
|
||||
<document>
|
||||
<paragraph>
|
||||
<link>
|
||||
<hashtag>
|
||||
one
|
||||
</hashtag>
|
||||
<hashtag>one</hashtag>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -23,9 +22,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -24,9 +23,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -37,9 +36,7 @@ export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<link thing="value">
|
||||
one
|
||||
</link>
|
||||
<link thing="value">one</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -24,9 +23,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -23,9 +22,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -36,9 +35,7 @@ export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<link>
|
||||
one
|
||||
</link>
|
||||
<link>one</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -30,9 +29,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -30,9 +29,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -43,7 +42,9 @@ export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
o<i>n<b>e</b></i>
|
||||
o<i>
|
||||
n<b>e</b>
|
||||
</i>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -24,9 +23,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -23,9 +22,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -11,7 +10,7 @@ export const config = {
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
deserialize(el, next) {
|
||||
@@ -19,9 +18,9 @@ export const config = {
|
||||
object: 'block',
|
||||
type: 'quote',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -16,9 +15,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import h from '../helpers/h'
|
||||
@@ -26,9 +25,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
export const config = {
|
||||
rules: [
|
||||
{
|
||||
@@ -12,9 +11,9 @@ export const config = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const input = `
|
||||
@@ -37,13 +36,13 @@ export const output = {
|
||||
{
|
||||
object: 'leaf',
|
||||
text: 'one',
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const options = {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { createHyperscript } from 'slate-hyperscript'
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,7 @@ const h = createHyperscript({
|
||||
image: {
|
||||
type: 'image',
|
||||
isVoid: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
inlines: {
|
||||
link: 'link',
|
||||
@@ -25,7 +24,7 @@ const h = createHyperscript({
|
||||
emoji: {
|
||||
type: 'emoji',
|
||||
isVoid: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
marks: {
|
||||
b: 'bold',
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
@@ -25,7 +24,10 @@ beforeEach(() => {
|
||||
describe('slate-html-serializer', () => {
|
||||
describe('deserialize()', () => {
|
||||
const dir = resolve(__dirname, './deserialize')
|
||||
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t)))
|
||||
const tests = fs
|
||||
.readdirSync(dir)
|
||||
.filter(t => t[0] != '.')
|
||||
.map(t => basename(t, extname(t)))
|
||||
|
||||
for (const test of tests) {
|
||||
it(test, async () => {
|
||||
@@ -42,7 +44,10 @@ describe('slate-html-serializer', () => {
|
||||
|
||||
describe('serialize()', () => {
|
||||
const dir = resolve(__dirname, './serialize')
|
||||
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t)))
|
||||
const tests = fs
|
||||
.readdirSync(dir)
|
||||
.filter(t => t[0] != '.')
|
||||
.map(t => basename(t, extname(t)))
|
||||
|
||||
for (const test of tests) {
|
||||
it(test, async () => {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -9,20 +8,20 @@ export const rules = [
|
||||
serialize(obj, children) {
|
||||
if (obj.object != 'block') return
|
||||
switch (obj.type) {
|
||||
case 'paragraph': return React.createElement('p', {}, children)
|
||||
case 'quote': return React.createElement('blockquote', {}, children)
|
||||
case 'paragraph':
|
||||
return React.createElement('p', {}, children)
|
||||
case 'quote':
|
||||
return React.createElement('blockquote', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<quote>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</quote>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -8,18 +7,20 @@ export const rules = [
|
||||
{
|
||||
serialize(obj, children) {
|
||||
if (obj.object == 'block' && obj.type == 'paragraph') {
|
||||
return React.createElement('p', { 'data-thing': obj.data.get('thing') }, children)
|
||||
return React.createElement(
|
||||
'p',
|
||||
{ 'data-thing': obj.data.get('thing') },
|
||||
children
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph thing="value">
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph thing="value">one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -10,8 +9,8 @@ export const rules = [
|
||||
if (obj.object == 'block' && obj.type == 'image') {
|
||||
return React.createElement('img')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -14,8 +13,8 @@ export const rules = [
|
||||
if (obj.object == 'mark' && obj.type == 'bold') {
|
||||
return React.createElement('strong', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -10,16 +9,14 @@ export const rules = [
|
||||
if (obj.object == 'block' && obj.type == 'paragraph') {
|
||||
return React.createElement('p', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -18,8 +17,8 @@ export const rules = [
|
||||
if (obj.object == 'inline' && obj.type == 'hashtag') {
|
||||
return React.createElement('span', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
@@ -27,9 +26,7 @@ export const input = (
|
||||
<document>
|
||||
<paragraph>
|
||||
<link>
|
||||
<hashtag>
|
||||
one
|
||||
</hashtag>
|
||||
<hashtag>one</hashtag>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -12,19 +11,21 @@ export const rules = [
|
||||
}
|
||||
|
||||
if (obj.object == 'inline' && obj.type == 'link') {
|
||||
return React.createElement('a', { href: obj.data.get('href') }, children)
|
||||
return React.createElement(
|
||||
'a',
|
||||
{ href: obj.data.get('href') },
|
||||
children
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<link href="https://google.com">
|
||||
one
|
||||
</link>
|
||||
<link href="https://google.com">one</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -14,8 +13,8 @@ export const rules = [
|
||||
if (obj.object == 'inline' && obj.type == 'emoji') {
|
||||
return React.createElement('img')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -18,8 +17,8 @@ export const rules = [
|
||||
if (obj.object == 'mark' && obj.type == 'bold') {
|
||||
return React.createElement('strong', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -14,17 +13,15 @@ export const rules = [
|
||||
if (obj.object == 'inline' && obj.type == 'link') {
|
||||
return React.createElement('a', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<link>
|
||||
one
|
||||
</link>
|
||||
<link>one</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/** @jsx h */
|
||||
|
||||
import React from 'react'
|
||||
@@ -7,23 +6,21 @@ import h from '../helpers/h'
|
||||
export const rules = [
|
||||
{},
|
||||
{
|
||||
serialize(obj, children) {}
|
||||
serialize(obj, children) {},
|
||||
},
|
||||
{
|
||||
serialize(obj, children) {
|
||||
if (obj.object == 'block' && obj.type == 'paragraph') {
|
||||
return React.createElement('p', {}, children)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,17 +1,7 @@
|
||||
|
||||
import isEmpty from 'is-empty'
|
||||
import isPlainObject from 'is-plain-object'
|
||||
|
||||
import {
|
||||
Block,
|
||||
Document,
|
||||
Inline,
|
||||
Mark,
|
||||
Node,
|
||||
Range,
|
||||
Text,
|
||||
Value,
|
||||
} from 'slate'
|
||||
import { Block, Document, Inline, Mark, Node, Range, Text, Value } from 'slate'
|
||||
|
||||
/**
|
||||
* Create selection point constants, for comparison by reference.
|
||||
@@ -30,7 +20,6 @@ const FOCUS = {}
|
||||
*/
|
||||
|
||||
const CREATORS = {
|
||||
|
||||
anchor(tagName, attributes, children) {
|
||||
return ANCHOR
|
||||
},
|
||||
@@ -83,7 +72,7 @@ const CREATORS = {
|
||||
// Search the document's texts to see if any of them have the anchor or
|
||||
// focus information saved, so we can set the selection.
|
||||
if (document) {
|
||||
document.getTexts().forEach((text) => {
|
||||
document.getTexts().forEach(text => {
|
||||
if (text.__anchor != null) {
|
||||
props.anchorKey = text.key
|
||||
props.anchorOffset = text.__anchor
|
||||
@@ -99,11 +88,15 @@ const CREATORS = {
|
||||
}
|
||||
|
||||
if (props.anchorKey && !props.focusKey) {
|
||||
throw new Error(`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<anchor/>\`. For collapsed selections, use \`<cursor/>\`.`)
|
||||
throw new Error(
|
||||
`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<anchor/>\`. For collapsed selections, use \`<cursor/>\`.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!props.anchorKey && props.focusKey) {
|
||||
throw new Error(`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<focus/>\`. For collapsed selections, use \`<cursor/>\`.`)
|
||||
throw new Error(
|
||||
`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<focus/>\`. For collapsed selections, use \`<cursor/>\`.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!isEmpty(props)) {
|
||||
@@ -118,7 +111,6 @@ const CREATORS = {
|
||||
const nodes = createChildren(children, { key: attributes.key })
|
||||
return nodes
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,11 +176,12 @@ function createChildren(children, options = {}) {
|
||||
node = next
|
||||
}
|
||||
|
||||
children.forEach((child) => {
|
||||
children.forEach(child => {
|
||||
// If the child is a non-text node, push the current node and the new child
|
||||
// onto the array, then creating a new node for future selection tracking.
|
||||
if (Node.isNode(child) && !Text.isText(child)) {
|
||||
if (node.text.length || node.__anchor != null || node.__focus != null) array.push(node)
|
||||
if (node.text.length || node.__anchor != null || node.__focus != null)
|
||||
array.push(node)
|
||||
array.push(child)
|
||||
node = Text.create()
|
||||
length = 0
|
||||
@@ -211,7 +204,7 @@ function createChildren(children, options = {}) {
|
||||
setNode(node.set('key', child.key))
|
||||
}
|
||||
|
||||
child.getLeaves().forEach((leaf) => {
|
||||
child.getLeaves().forEach(leaf => {
|
||||
let { marks } = leaf
|
||||
if (options.marks) marks = marks.union(options.marks)
|
||||
setNode(node.insertText(i, leaf.text, marks))
|
||||
@@ -243,26 +236,22 @@ function createChildren(children, options = {}) {
|
||||
*/
|
||||
|
||||
function resolveCreators(options) {
|
||||
const {
|
||||
blocks = {},
|
||||
inlines = {},
|
||||
marks = {},
|
||||
} = options
|
||||
const { blocks = {}, inlines = {}, marks = {} } = options
|
||||
|
||||
const creators = {
|
||||
...CREATORS,
|
||||
...(options.creators || {}),
|
||||
}
|
||||
|
||||
Object.keys(blocks).map((key) => {
|
||||
Object.keys(blocks).map(key => {
|
||||
creators[key] = normalizeNode(key, blocks[key], 'block')
|
||||
})
|
||||
|
||||
Object.keys(inlines).map((key) => {
|
||||
Object.keys(inlines).map(key => {
|
||||
creators[key] = normalizeNode(key, inlines[key], 'inline')
|
||||
})
|
||||
|
||||
Object.keys(marks).map((key) => {
|
||||
Object.keys(marks).map(key => {
|
||||
creators[key] = normalizeMark(key, marks[key])
|
||||
})
|
||||
|
||||
@@ -297,14 +286,16 @@ function normalizeNode(key, value, object) {
|
||||
data: {
|
||||
...(value.data || {}),
|
||||
...rest,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return CREATORS[object](tagName, attrs, children)
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Slate hyperscript ${object} creators can be either functions, objects or strings, but you passed: ${value}`)
|
||||
throw new Error(
|
||||
`Slate hyperscript ${object} creators can be either functions, objects or strings, but you passed: ${value}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,14 +322,16 @@ function normalizeMark(key, value) {
|
||||
data: {
|
||||
...(value.data || {}),
|
||||
...attributes,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return CREATORS.mark(tagName, attrs, children)
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Slate hyperscript mark creators can be either functions, objects or strings, but you passed: ${value}`)
|
||||
throw new Error(
|
||||
`Slate hyperscript mark creators can be either functions, objects or strings, but you passed: ${value}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -9,9 +9,11 @@ import { __clear } from '../../slate/lib/utils/memoize'
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs.readdirSync(categoryDir).filter(c => c[0] != '.' && c != 'index.js')
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach((category) => {
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 50)
|
||||
set('mintime', 1000)
|
||||
@@ -23,9 +25,12 @@ categories.forEach((category) => {
|
||||
}
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs.readdirSync(benchmarkDir).filter(b => b[0] != '.' && !!~b.indexOf('.js')).map(b => basename(b, extname(b)))
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach((benchmark) => {
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
|
@@ -3,10 +3,12 @@
|
||||
|
||||
import Plain from '../..'
|
||||
|
||||
export default function (string) {
|
||||
export default function(string) {
|
||||
Plain.deserialize(string)
|
||||
}
|
||||
|
||||
export const input = `
|
||||
This is editable plain text, just like a text area.
|
||||
`.trim().repeat(10)
|
||||
`
|
||||
.trim()
|
||||
.repeat(10)
|
||||
|
@@ -4,7 +4,7 @@
|
||||
import Plain from '../..'
|
||||
import h from '../../test/helpers/h'
|
||||
|
||||
export default function (state) {
|
||||
export default function(state) {
|
||||
Plain.serialize(state)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ export const input = (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a textarea!
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { Block, Mark, Node, Value } from 'slate'
|
||||
import { Set } from 'immutable'
|
||||
|
||||
@@ -14,11 +13,7 @@ import { Set } from 'immutable'
|
||||
*/
|
||||
|
||||
function deserialize(string, options = {}) {
|
||||
let {
|
||||
defaultBlock = 'line',
|
||||
defaultMarks = [],
|
||||
toJSON = false,
|
||||
} = options
|
||||
let { defaultBlock = 'line', defaultMarks = [], toJSON = false } = options
|
||||
|
||||
if (Set.isSet(defaultMarks)) {
|
||||
defaultMarks = defaultMarks.toArray()
|
||||
@@ -32,7 +27,7 @@ function deserialize(string, options = {}) {
|
||||
document: {
|
||||
object: 'document',
|
||||
data: {},
|
||||
nodes: string.split('\n').map((line) => {
|
||||
nodes: string.split('\n').map(line => {
|
||||
return {
|
||||
...defaultBlock,
|
||||
object: 'block',
|
||||
@@ -46,13 +41,13 @@ function deserialize(string, options = {}) {
|
||||
object: 'leaf',
|
||||
text: line,
|
||||
marks: defaultMarks,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const ret = toJSON ? json : Value.fromJSON(json)
|
||||
@@ -79,7 +74,7 @@ function serialize(value) {
|
||||
|
||||
function serializeNode(node) {
|
||||
if (
|
||||
(node.object == 'document') ||
|
||||
node.object == 'document' ||
|
||||
(node.object == 'block' && Block.isBlockList(node.nodes))
|
||||
) {
|
||||
return node.nodes.map(serializeNode).join('\n')
|
||||
@@ -96,5 +91,5 @@ function serializeNode(node) {
|
||||
|
||||
export default {
|
||||
deserialize,
|
||||
serialize
|
||||
serialize,
|
||||
}
|
||||
|
@@ -10,12 +10,8 @@ two
|
||||
export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<line>
|
||||
one
|
||||
</line>
|
||||
<line>
|
||||
two
|
||||
</line>
|
||||
<line>one</line>
|
||||
<line>two</line>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -9,9 +9,7 @@ one
|
||||
export const output = (
|
||||
<value>
|
||||
<document>
|
||||
<line>
|
||||
one
|
||||
</line>
|
||||
<line>one</line>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
export const input = `
|
||||
one
|
||||
`.trim()
|
||||
@@ -22,15 +21,15 @@ export const output = {
|
||||
object: 'leaf',
|
||||
text: 'one',
|
||||
marks: [],
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const options = {
|
||||
toJSON: true
|
||||
toJSON: true,
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { createHyperscript } from 'slate-hyperscript'
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,7 @@ const h = createHyperscript({
|
||||
image: {
|
||||
type: 'image',
|
||||
isVoid: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
inlines: {
|
||||
link: 'link',
|
||||
@@ -25,7 +24,7 @@ const h = createHyperscript({
|
||||
emoji: {
|
||||
type: 'emoji',
|
||||
isVoid: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
marks: {
|
||||
b: 'bold',
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
@@ -24,7 +23,10 @@ beforeEach(() => {
|
||||
describe('slate-plain-serializer', () => {
|
||||
describe('deserialize()', () => {
|
||||
const dir = resolve(__dirname, './deserialize')
|
||||
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t)))
|
||||
const tests = fs
|
||||
.readdirSync(dir)
|
||||
.filter(t => t[0] != '.')
|
||||
.map(t => basename(t, extname(t)))
|
||||
|
||||
for (const test of tests) {
|
||||
it(test, async () => {
|
||||
@@ -40,7 +42,10 @@ describe('slate-plain-serializer', () => {
|
||||
|
||||
describe('serialize()', () => {
|
||||
const dir = resolve(__dirname, './serialize')
|
||||
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t)))
|
||||
const tests = fs
|
||||
.readdirSync(dir)
|
||||
.filter(t => t[0] != '.')
|
||||
.map(t => basename(t, extname(t)))
|
||||
|
||||
for (const test of tests) {
|
||||
it(test, async () => {
|
||||
|
@@ -5,13 +5,9 @@ import h from '../helpers/h'
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<paragraph />
|
||||
<paragraph>
|
||||
three
|
||||
</paragraph>
|
||||
<paragraph>three</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -5,15 +5,9 @@ import h from '../helpers/h'
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
two
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
three
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<paragraph>two</paragraph>
|
||||
<paragraph>three</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -6,18 +6,12 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<quote>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
two
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<paragraph>two</paragraph>
|
||||
</quote>
|
||||
<quote>
|
||||
<paragraph />
|
||||
<paragraph>
|
||||
four
|
||||
</paragraph>
|
||||
<paragraph>four</paragraph>
|
||||
</quote>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -6,20 +6,12 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<quote>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
two
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<paragraph>two</paragraph>
|
||||
</quote>
|
||||
<quote>
|
||||
<paragraph>
|
||||
three
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
four
|
||||
</paragraph>
|
||||
<paragraph>three</paragraph>
|
||||
<paragraph>four</paragraph>
|
||||
</quote>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -7,20 +7,12 @@ export const input = (
|
||||
<document>
|
||||
<quote>
|
||||
<quote>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
two
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<paragraph>two</paragraph>
|
||||
</quote>
|
||||
<quote>
|
||||
<paragraph>
|
||||
three
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
four
|
||||
</paragraph>
|
||||
<paragraph>three</paragraph>
|
||||
<paragraph>four</paragraph>
|
||||
</quote>
|
||||
</quote>
|
||||
</document>
|
||||
|
@@ -6,14 +6,10 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<quote>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
<paragraph>
|
||||
<link>
|
||||
<hashtag>
|
||||
two
|
||||
</hashtag>
|
||||
<hashtag>two</hashtag>
|
||||
</link>
|
||||
</paragraph>
|
||||
</quote>
|
||||
|
@@ -7,14 +7,10 @@ export const input = (
|
||||
<document>
|
||||
<quote>
|
||||
<paragraph>
|
||||
<link>
|
||||
one
|
||||
</link>
|
||||
<link>one</link>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<link>
|
||||
two
|
||||
</link>
|
||||
<link>two</link>
|
||||
</paragraph>
|
||||
</quote>
|
||||
</document>
|
||||
|
@@ -5,9 +5,7 @@ import h from '../helpers/h'
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph thing="value">
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph thing="value">one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -5,9 +5,7 @@ import h from '../helpers/h'
|
||||
export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
one
|
||||
</paragraph>
|
||||
<paragraph>one</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
)
|
||||
|
@@ -7,9 +7,7 @@ export const input = (
|
||||
<document>
|
||||
<paragraph>
|
||||
<link>
|
||||
<hashtag>
|
||||
one
|
||||
</hashtag>
|
||||
<hashtag>one</hashtag>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
|
@@ -6,9 +6,7 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<link thing="value">
|
||||
one
|
||||
</link>
|
||||
<link thing="value">one</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -6,9 +6,7 @@ export const input = (
|
||||
<value>
|
||||
<document>
|
||||
<paragraph>
|
||||
<link>
|
||||
one
|
||||
</link>
|
||||
<link>one</link>
|
||||
</paragraph>
|
||||
</document>
|
||||
</value>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
Block,
|
||||
Change,
|
||||
@@ -29,16 +28,21 @@ function create(name, validate) {
|
||||
function check(isRequired, props, propName, componentName, location) {
|
||||
const value = props[propName]
|
||||
if (value == null && !isRequired) return null
|
||||
if (value == null && isRequired) return new Error(`The ${location} \`${propName}\` is marked as required in \`${componentName}\`, but it was not supplied.`)
|
||||
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}`)
|
||||
return new Error(
|
||||
`Invalid ${location} \`${propName}\` supplied to \`${componentName}\`, expected a Slate \`${name}\` but received: ${value}`
|
||||
)
|
||||
}
|
||||
|
||||
function propType(...args) {
|
||||
return check(false, ...args)
|
||||
}
|
||||
|
||||
propType.isRequired = function (...args) {
|
||||
propType.isRequired = function(...args) {
|
||||
return check(true, ...args)
|
||||
}
|
||||
|
||||
|
@@ -8,17 +8,22 @@ import { basename, extname, resolve } from 'path'
|
||||
*/
|
||||
|
||||
const categoryDir = resolve(__dirname)
|
||||
const categories = fs.readdirSync(categoryDir).filter(c => c[0] != '.' && c != 'index.js')
|
||||
const categories = fs
|
||||
.readdirSync(categoryDir)
|
||||
.filter(c => c[0] != '.' && c != 'index.js')
|
||||
|
||||
categories.forEach((category) => {
|
||||
categories.forEach(category => {
|
||||
suite(category, () => {
|
||||
set('iterations', 50)
|
||||
set('mintime', 1000)
|
||||
|
||||
const benchmarkDir = resolve(categoryDir, category)
|
||||
const benchmarks = fs.readdirSync(benchmarkDir).filter(b => b[0] != '.' && !!~b.indexOf('.js')).map(b => basename(b, extname(b)))
|
||||
const benchmarks = fs
|
||||
.readdirSync(benchmarkDir)
|
||||
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
|
||||
.map(b => basename(b, extname(b)))
|
||||
|
||||
benchmarks.forEach((benchmark) => {
|
||||
benchmarks.forEach(benchmark => {
|
||||
const dir = resolve(benchmarkDir, benchmark)
|
||||
const module = require(dir)
|
||||
const fn = module.default
|
||||
|
@@ -6,7 +6,7 @@ import ReactDOM from 'react-dom/server'
|
||||
import h from '../../test/helpers/h'
|
||||
import { Editor } from '../..'
|
||||
|
||||
export default function (value) {
|
||||
export default function(value) {
|
||||
const el = React.createElement(Editor, { value })
|
||||
ReactDOM.renderToStaticMarkup(el)
|
||||
}
|
||||
@@ -18,7 +18,8 @@ export const input = (
|
||||
<quote>
|
||||
<paragraph>
|
||||
<paragraph>
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a textarea!
|
||||
This is editable <b>rich</b> text, <i>much</i> better than a
|
||||
textarea!
|
||||
</paragraph>
|
||||
</paragraph>
|
||||
</quote>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import React from 'react'
|
||||
import Types from 'prop-types'
|
||||
@@ -15,7 +14,7 @@ import {
|
||||
IS_FIREFOX,
|
||||
IS_IOS,
|
||||
IS_ANDROID,
|
||||
SUPPORTED_EVENTS
|
||||
SUPPORTED_EVENTS,
|
||||
} from '../constants/environment'
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,6 @@ const debug = Debug('slate:content')
|
||||
*/
|
||||
|
||||
class Content extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
@@ -77,8 +75,8 @@ class Content extends React.Component {
|
||||
this.tmp.key = 0
|
||||
this.tmp.isUpdatingSelection = false
|
||||
|
||||
EVENT_HANDLERS.forEach((handler) => {
|
||||
this[handler] = (event) => {
|
||||
EVENT_HANDLERS.forEach(handler => {
|
||||
this[handler] = event => {
|
||||
this.onEvent(handler, event)
|
||||
}
|
||||
})
|
||||
@@ -96,7 +94,10 @@ class Content extends React.Component {
|
||||
const { editor } = this.props
|
||||
const window = getWindow(this.element)
|
||||
|
||||
window.document.addEventListener('selectionchange', this.onNativeSelectionChange)
|
||||
window.document.addEventListener(
|
||||
'selectionchange',
|
||||
this.onNativeSelectionChange
|
||||
)
|
||||
|
||||
// COMPAT: Restrict scope of `beforeinput` to mobile.
|
||||
if ((IS_IOS || IS_ANDROID) && SUPPORTED_EVENTS.beforeinput) {
|
||||
@@ -118,7 +119,10 @@ class Content extends React.Component {
|
||||
const window = getWindow(this.element)
|
||||
|
||||
if (window) {
|
||||
window.document.removeEventListener('selectionchange', this.onNativeSelectionChange)
|
||||
window.document.removeEventListener(
|
||||
'selectionchange',
|
||||
this.onNativeSelectionChange
|
||||
)
|
||||
}
|
||||
|
||||
// COMPAT: Restrict scope of `beforeinput` to mobile.
|
||||
@@ -169,16 +173,14 @@ class Content extends React.Component {
|
||||
const range = findDOMRange(selection, window)
|
||||
|
||||
if (!range) {
|
||||
logger.error('Unable to find a native DOM range from the current selection.', { selection })
|
||||
logger.error(
|
||||
'Unable to find a native DOM range from the current selection.',
|
||||
{ selection }
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const {
|
||||
startContainer,
|
||||
startOffset,
|
||||
endContainer,
|
||||
endOffset,
|
||||
} = range
|
||||
const { startContainer, startOffset, endContainer, endOffset } = range
|
||||
|
||||
// If the new range matches the current selection, there is nothing to fix.
|
||||
// COMPAT: The native `Range` object always has it's "start" first and "end"
|
||||
@@ -186,18 +188,14 @@ class Content extends React.Component {
|
||||
// to check both orientations here. (2017/10/31)
|
||||
if (current) {
|
||||
if (
|
||||
(
|
||||
startContainer == current.startContainer &&
|
||||
(startContainer == current.startContainer &&
|
||||
startOffset == current.startOffset &&
|
||||
endContainer == current.endContainer &&
|
||||
endOffset == current.endOffset
|
||||
) ||
|
||||
(
|
||||
startContainer == current.endContainer &&
|
||||
endOffset == current.endOffset) ||
|
||||
(startContainer == current.endContainer &&
|
||||
startOffset == current.endOffset &&
|
||||
endContainer == current.startContainer &&
|
||||
endOffset == current.startOffset
|
||||
)
|
||||
endOffset == current.startOffset)
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -251,7 +249,7 @@ class Content extends React.Component {
|
||||
* @param {Element} element
|
||||
*/
|
||||
|
||||
ref = (element) => {
|
||||
ref = element => {
|
||||
this.element = element
|
||||
}
|
||||
|
||||
@@ -264,13 +262,13 @@ class Content extends React.Component {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
isInEditor = (target) => {
|
||||
isInEditor = target => {
|
||||
const { element } = this
|
||||
// COMPAT: Text nodes don't have `isContentEditable` property. So, when
|
||||
// `target` is a text node use its parent node for check.
|
||||
const el = target.nodeType === 3 ? target.parentNode : target
|
||||
return (
|
||||
(el.isContentEditable) &&
|
||||
el.isContentEditable &&
|
||||
(el === element || el.closest('[data-slate-editor]') === element)
|
||||
)
|
||||
}
|
||||
@@ -295,11 +293,7 @@ class Content extends React.Component {
|
||||
// programmatically while updating selection.
|
||||
if (
|
||||
this.tmp.isUpdatingSelection &&
|
||||
(
|
||||
handler == 'onSelect' ||
|
||||
handler == 'onBlur' ||
|
||||
handler == 'onFocus'
|
||||
)
|
||||
(handler == 'onSelect' || handler == 'onBlur' || handler == 'onFocus')
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -368,11 +362,11 @@ class Content extends React.Component {
|
||||
* @param {InputEvent} event
|
||||
*/
|
||||
|
||||
onNativeBeforeInput = (event) => {
|
||||
onNativeBeforeInput = event => {
|
||||
if (this.props.readOnly) return
|
||||
if (!this.isInEditor(event.target)) return
|
||||
|
||||
const [ targetRange ] = event.getTargetRanges()
|
||||
const [targetRange] = event.getTargetRanges()
|
||||
if (!targetRange) return
|
||||
|
||||
const { editor } = this.props
|
||||
@@ -391,7 +385,7 @@ class Content extends React.Component {
|
||||
event.preventDefault()
|
||||
const range = findRange(targetRange, editor.value)
|
||||
|
||||
editor.change((change) => {
|
||||
editor.change(change => {
|
||||
if (change.value.isInVoid) {
|
||||
change.collapseToStartOfNextText()
|
||||
} else {
|
||||
@@ -407,9 +401,10 @@ class Content extends React.Component {
|
||||
// `dataTransfer` should have the text for the `insertReplacementText`
|
||||
// input type, but Safari uses `insertText` for spell check replacements
|
||||
// and sets `data` to `null`.
|
||||
const text = event.data == null
|
||||
? event.dataTransfer.getData('text/plain')
|
||||
: event.data
|
||||
const text =
|
||||
event.data == null
|
||||
? event.dataTransfer.getData('text/plain')
|
||||
: event.data
|
||||
|
||||
if (text == null) return
|
||||
|
||||
@@ -419,7 +414,7 @@ class Content extends React.Component {
|
||||
const { selection } = value
|
||||
const range = findRange(targetRange, value)
|
||||
|
||||
editor.change((change) => {
|
||||
editor.change(change => {
|
||||
change.insertTextAtRange(range, text, selection.marks)
|
||||
|
||||
// If the text was successfully inserted, and the selection had marks
|
||||
@@ -443,7 +438,7 @@ class Content extends React.Component {
|
||||
* @param {Event} event
|
||||
*/
|
||||
|
||||
onNativeSelectionChange = throttle((event) => {
|
||||
onNativeSelectionChange = throttle(event => {
|
||||
if (this.props.readOnly) return
|
||||
|
||||
const window = getWindow(event.target)
|
||||
@@ -526,7 +521,7 @@ class Content extends React.Component {
|
||||
autoCorrect={props.autoCorrect ? 'on' : 'off'}
|
||||
spellCheck={spellCheck}
|
||||
style={style}
|
||||
role={readOnly ? null : (role || 'textbox')}
|
||||
role={readOnly ? null : role || 'textbox'}
|
||||
tabIndex={tabIndex}
|
||||
// COMPAT: The Grammarly Chrome extension works by changing the DOM out
|
||||
// from under `contenteditable` elements, which leads to weird behaviors
|
||||
@@ -567,14 +562,13 @@ class Content extends React.Component {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Mix in handler prop types.
|
||||
*/
|
||||
|
||||
EVENT_HANDLERS.forEach((handler) => {
|
||||
EVENT_HANDLERS.forEach(handler => {
|
||||
Content.propTypes[handler] = Types.func.isRequired
|
||||
})
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import Portal from 'react-portal'
|
||||
import React from 'react'
|
||||
@@ -28,7 +27,6 @@ const debug = Debug('slate:editor')
|
||||
*/
|
||||
|
||||
class Editor extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
@@ -95,7 +93,7 @@ class Editor extends React.Component {
|
||||
this.state.value = change.value
|
||||
|
||||
// Create a bound event handler for each event.
|
||||
EVENT_HANDLERS.forEach((handler) => {
|
||||
EVENT_HANDLERS.forEach(handler => {
|
||||
this[handler] = (...args) => {
|
||||
this.onEvent(handler, ...args)
|
||||
}
|
||||
@@ -109,7 +107,7 @@ class Editor extends React.Component {
|
||||
* @param {Object} props
|
||||
*/
|
||||
|
||||
componentWillReceiveProps = (props) => {
|
||||
componentWillReceiveProps = props => {
|
||||
let { schema, stack } = this
|
||||
|
||||
// Increment the updates counter as a baseline.
|
||||
@@ -117,7 +115,10 @@ class Editor extends React.Component {
|
||||
|
||||
// If the plugins or the schema have changed, we need to re-resolve the
|
||||
// plugins, since it will result in a new stack and new validations.
|
||||
if (props.plugins != this.props.plugins || props.schema != this.props.schema) {
|
||||
if (
|
||||
props.plugins != this.props.plugins ||
|
||||
props.schema != this.props.schema
|
||||
) {
|
||||
const plugins = this.resolvePlugins(props.plugins, props.schema)
|
||||
stack = Stack.create({ plugins })
|
||||
schema = Schema.create({ plugins })
|
||||
@@ -129,7 +130,9 @@ class Editor extends React.Component {
|
||||
// If we've resolved a few times already, and it's exactly in line with
|
||||
// the updates, then warn the user that they may be doing something wrong.
|
||||
if (this.tmp.resolves > 5 && this.tmp.resolves == this.tmp.updates) {
|
||||
logger.warn('A Slate <Editor> is re-resolving `props.plugins` or `props.schema` on each update, which leads to poor performance. This is often due to passing in a new `schema` or `plugins` prop with each render by declaring them inline in your render function. Do not do this!')
|
||||
logger.warn(
|
||||
'A Slate <Editor> is re-resolving `props.plugins` or `props.schema` on each update, which leads to poor performance. This is often due to passing in a new `schema` or `plugins` prop with each render by declaring them inline in your render function. Do not do this!'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +169,7 @@ class Editor extends React.Component {
|
||||
* @param {Change} change
|
||||
*/
|
||||
|
||||
queueChange = (change) => {
|
||||
queueChange = change => {
|
||||
if (change.operations.size) {
|
||||
debug('queueChange', { change })
|
||||
this.tmp.change = change
|
||||
@@ -239,7 +242,7 @@ class Editor extends React.Component {
|
||||
*/
|
||||
|
||||
onEvent = (handler, event) => {
|
||||
this.change((change) => {
|
||||
this.change(change => {
|
||||
this.stack.run(handler, event, change, this)
|
||||
})
|
||||
}
|
||||
@@ -250,7 +253,7 @@ class Editor extends React.Component {
|
||||
* @param {Change} change
|
||||
*/
|
||||
|
||||
onChange = (change) => {
|
||||
onChange = change => {
|
||||
debug('onChange', { change })
|
||||
|
||||
this.stack.run('onChange', change, this)
|
||||
@@ -271,7 +274,11 @@ class Editor extends React.Component {
|
||||
|
||||
const children = this.stack
|
||||
.map('renderPortal', this.value, this)
|
||||
.map((child, i) => <Portal key={i} isOpened>{child}</Portal>)
|
||||
.map((child, i) => (
|
||||
<Portal key={i} isOpened>
|
||||
{child}
|
||||
</Portal>
|
||||
))
|
||||
|
||||
const props = { ...this.props, children }
|
||||
const tree = this.stack.render('renderEditor', props, this)
|
||||
@@ -296,7 +303,7 @@ class Editor extends React.Component {
|
||||
const beforePlugin = BeforePlugin()
|
||||
const afterPlugin = AfterPlugin()
|
||||
const editorPlugin = {
|
||||
schema: schema || {}
|
||||
schema: schema || {},
|
||||
}
|
||||
|
||||
for (const prop of PLUGINS_PROPS) {
|
||||
@@ -313,17 +320,10 @@ class Editor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
beforePlugin,
|
||||
editorPlugin,
|
||||
...(plugins || []),
|
||||
afterPlugin
|
||||
]
|
||||
return [beforePlugin, editorPlugin, ...(plugins || []), afterPlugin]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mix in the property types for the event handlers.
|
||||
*/
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import React from 'react'
|
||||
import Types from 'prop-types'
|
||||
@@ -21,7 +20,6 @@ const debug = Debug('slate:leaves')
|
||||
*/
|
||||
|
||||
class Leaf extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
@@ -85,14 +83,10 @@ class Leaf extends React.Component {
|
||||
const { node, index } = this.props
|
||||
const offsetKey = OffsetKey.stringify({
|
||||
key: node.key,
|
||||
index
|
||||
index,
|
||||
})
|
||||
|
||||
return (
|
||||
<span data-offset-key={offsetKey}>
|
||||
{this.renderMarks()}
|
||||
</span>
|
||||
)
|
||||
return <span data-offset-key={offsetKey}>{this.renderMarks()}</span>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,7 +136,6 @@ class Leaf extends React.Component {
|
||||
// Otherwise, just return the text.
|
||||
return text
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import ImmutableTypes from 'react-immutable-proptypes'
|
||||
import React from 'react'
|
||||
@@ -24,7 +23,6 @@ const debug = Debug('slate:node')
|
||||
*/
|
||||
|
||||
class Node extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
@@ -62,10 +60,14 @@ class Node extends React.Component {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
shouldComponentUpdate = (nextProps) => {
|
||||
shouldComponentUpdate = nextProps => {
|
||||
const { props } = this
|
||||
const { stack } = props.editor
|
||||
const shouldUpdate = stack.find('shouldNodeComponentUpdate', props, nextProps)
|
||||
const shouldUpdate = stack.find(
|
||||
'shouldNodeComponentUpdate',
|
||||
props,
|
||||
nextProps
|
||||
)
|
||||
const n = nextProps
|
||||
const p = props
|
||||
|
||||
@@ -78,7 +80,9 @@ class Node extends React.Component {
|
||||
}
|
||||
|
||||
if (shouldUpdate === false) {
|
||||
logger.warn('Returning false in `shouldNodeComponentUpdate` does not disable Slate\'s internal `shouldComponentUpdate` logic. If you want to prevent updates, use React\'s `shouldComponentUpdate` instead.')
|
||||
logger.warn(
|
||||
"Returning false in `shouldNodeComponentUpdate` does not disable Slate's internal `shouldComponentUpdate` logic. If you want to prevent updates, use React's `shouldComponentUpdate` instead."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,15 +152,19 @@ class Node extends React.Component {
|
||||
let placeholder = stack.find('renderPlaceholder', props)
|
||||
|
||||
if (placeholder) {
|
||||
placeholder = React.cloneElement(placeholder, { key: `${node.key}-placeholder` })
|
||||
placeholder = React.cloneElement(placeholder, {
|
||||
key: `${node.key}-placeholder`,
|
||||
})
|
||||
children = [placeholder, ...children]
|
||||
}
|
||||
|
||||
const element = stack.find('renderNode', { ...props, attributes, children })
|
||||
const element = stack.find('renderNode', {
|
||||
...props,
|
||||
attributes,
|
||||
children,
|
||||
})
|
||||
|
||||
return node.isVoid
|
||||
? <Void {...this.props}>{element}</Void>
|
||||
: element
|
||||
return node.isVoid ? <Void {...this.props}>{element}</Void> : element
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,7 +193,6 @@ class Node extends React.Component {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import ImmutableTypes from 'react-immutable-proptypes'
|
||||
import React from 'react'
|
||||
@@ -22,7 +21,6 @@ const debug = Debug('slate:node')
|
||||
*/
|
||||
|
||||
class Text extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
@@ -69,7 +67,7 @@ class Text extends React.Component {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
shouldComponentUpdate = (nextProps) => {
|
||||
shouldComponentUpdate = nextProps => {
|
||||
const { props } = this
|
||||
const n = nextProps
|
||||
const p = props
|
||||
@@ -109,7 +107,7 @@ class Text extends React.Component {
|
||||
const { document } = value
|
||||
const { key } = node
|
||||
|
||||
const decs = decorations.filter((d) => {
|
||||
const decs = decorations.filter(d => {
|
||||
const { startKey, endKey } = d
|
||||
if (startKey == key || endKey == key) return true
|
||||
const startsBefore = document.areDescendantsSorted(startKey, key)
|
||||
@@ -162,7 +160,6 @@ class Text extends React.Component {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import React from 'react'
|
||||
import SlateTypes from 'slate-prop-types'
|
||||
@@ -21,7 +20,6 @@ const debug = Debug('slate:void')
|
||||
*/
|
||||
|
||||
class Void extends React.Component {
|
||||
|
||||
/**
|
||||
* Property types.
|
||||
*
|
||||
@@ -78,11 +76,7 @@ class Void extends React.Component {
|
||||
</Tag>
|
||||
)
|
||||
|
||||
const content = (
|
||||
<Tag draggable={readOnly ? null : true}>
|
||||
{children}
|
||||
</Tag>
|
||||
)
|
||||
const content = <Tag draggable={readOnly ? null : true}>{children}</Tag>
|
||||
|
||||
this.debug('render', { props })
|
||||
|
||||
@@ -110,7 +104,14 @@ class Void extends React.Component {
|
||||
*/
|
||||
|
||||
renderText = () => {
|
||||
const { block, decorations, isSelected, node, readOnly, editor } = this.props
|
||||
const {
|
||||
block,
|
||||
decorations,
|
||||
isSelected,
|
||||
node,
|
||||
readOnly,
|
||||
editor,
|
||||
} = this.props
|
||||
const child = node.getFirstText()
|
||||
return (
|
||||
<Text
|
||||
@@ -125,7 +126,6 @@ class Void extends React.Component {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import browser from 'is-in-browser'
|
||||
|
||||
/**
|
||||
@@ -26,9 +25,7 @@ const BROWSER_RULES = [
|
||||
* @type {Array}
|
||||
*/
|
||||
|
||||
const EVENT_RULES = [
|
||||
['beforeinput', el => 'onbeforeinput' in el]
|
||||
]
|
||||
const EVENT_RULES = [['beforeinput', el => 'onbeforeinput' in el]]
|
||||
|
||||
/**
|
||||
* Operating system matching rules.
|
||||
@@ -59,14 +56,14 @@ let OS
|
||||
if (browser) {
|
||||
const { userAgent } = window.navigator
|
||||
|
||||
for (const [ name, regexp ] of BROWSER_RULES) {
|
||||
for (const [name, regexp] of BROWSER_RULES) {
|
||||
if (regexp.test(userAgent)) {
|
||||
BROWSER = name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for (const [ name, regexp ] of OS_RULES) {
|
||||
for (const [name, regexp] of OS_RULES) {
|
||||
if (regexp.test(userAgent)) {
|
||||
OS = name
|
||||
break
|
||||
@@ -76,7 +73,7 @@ if (browser) {
|
||||
const testEl = window.document.createElement('div')
|
||||
testEl.contentEditable = true
|
||||
|
||||
for (const [ name, testFn ] of EVENT_RULES) {
|
||||
for (const [name, testFn] of EVENT_RULES) {
|
||||
EVENTS[name] = testFn(testEl)
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Event handlers used by Slate plugins.
|
||||
*
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { isKeyHotkey } from 'is-hotkey'
|
||||
|
||||
import { IS_IOS, IS_MAC } from './environment'
|
||||
@@ -33,8 +32,10 @@ const DELETE_FORWARD = e => DELETE(e) || SHIFT_DELETE(e)
|
||||
|
||||
const DELETE_CHAR_BACKWARD_MAC = isKeyHotkey('ctrl+h')
|
||||
const DELETE_CHAR_FORWARD_MAC = isKeyHotkey('ctrl+d')
|
||||
const DELETE_CHAR_BACKWARD = e => DELETE_BACKWARD(e) || (IS_APPLE && DELETE_CHAR_BACKWARD_MAC(e))
|
||||
const DELETE_CHAR_FORWARD = e => DELETE_FORWARD(e) || (IS_APPLE && DELETE_CHAR_FORWARD_MAC(e))
|
||||
const DELETE_CHAR_BACKWARD = e =>
|
||||
DELETE_BACKWARD(e) || (IS_APPLE && DELETE_CHAR_BACKWARD_MAC(e))
|
||||
const DELETE_CHAR_FORWARD = e =>
|
||||
DELETE_FORWARD(e) || (IS_APPLE && DELETE_CHAR_FORWARD_MAC(e))
|
||||
|
||||
const DELETE_LINE_BACKWARD_MAC = isKeyHotkey('cmd+backspace')
|
||||
const DELETE_LINE_FORWARD_MAC = isKeyHotkey('ctrl+k')
|
||||
@@ -45,8 +46,10 @@ const DELETE_WORD_BACKWARD_MAC = isKeyHotkey('option+backspace')
|
||||
const DELETE_WORD_BACKWARD_PC = isKeyHotkey('ctrl+backspace')
|
||||
const DELETE_WORD_FORWARD_MAC = isKeyHotkey('option+delete')
|
||||
const DELETE_WORD_FORWARD_PC = isKeyHotkey('ctrl+delete')
|
||||
const DELETE_WORD_BACKWARD = e => IS_APPLE ? DELETE_WORD_BACKWARD_MAC(e) : DELETE_WORD_BACKWARD_PC(e)
|
||||
const DELETE_WORD_FORWARD = e => IS_APPLE ? DELETE_WORD_FORWARD_MAC(e) : DELETE_WORD_FORWARD_PC(e)
|
||||
const DELETE_WORD_BACKWARD = e =>
|
||||
IS_APPLE ? DELETE_WORD_BACKWARD_MAC(e) : DELETE_WORD_BACKWARD_PC(e)
|
||||
const DELETE_WORD_FORWARD = e =>
|
||||
IS_APPLE ? DELETE_WORD_FORWARD_MAC(e) : DELETE_WORD_FORWARD_PC(e)
|
||||
|
||||
const RIGHT_ARROW = isKeyHotkey('right')
|
||||
const LEFT_ARROW = isKeyHotkey('left')
|
||||
@@ -69,12 +72,12 @@ const EXTEND_LINE_FORWARD = e => IS_APPLE && EXTEND_LINE_FORWARD_MAC(e)
|
||||
const UNDO = isKeyHotkey('mod+z')
|
||||
const REDO_MAC = isKeyHotkey('mod+shift+z')
|
||||
const REDO_PC = isKeyHotkey('mod+y')
|
||||
const REDO = e => IS_APPLE ? REDO_MAC(e) : REDO_PC(e)
|
||||
const REDO = e => (IS_APPLE ? REDO_MAC(e) : REDO_PC(e))
|
||||
|
||||
const TRANSPOSE_CHARACTER_MAC = isKeyHotkey('ctrl+t')
|
||||
const TRANSPOSE_CHARACTER = e => IS_APPLE && TRANSPOSE_CHARACTER_MAC(e)
|
||||
|
||||
const CONTENTEDITABLE = e => (
|
||||
const CONTENTEDITABLE = e =>
|
||||
BOLD(e) ||
|
||||
DELETE_CHAR_BACKWARD(e) ||
|
||||
DELETE_CHAR_FORWARD(e) ||
|
||||
@@ -87,16 +90,14 @@ const CONTENTEDITABLE = e => (
|
||||
SPLIT_BLOCK(e) ||
|
||||
TRANSPOSE_CHARACTER(e) ||
|
||||
UNDO(e)
|
||||
)
|
||||
|
||||
const COMPOSING = e => (
|
||||
const COMPOSING = e =>
|
||||
e.key == 'ArrowDown' ||
|
||||
e.key == 'ArrowLeft' ||
|
||||
e.key == 'ArrowRight' ||
|
||||
e.key == 'ArrowUp' ||
|
||||
e.key == 'Backspace' ||
|
||||
e.key == 'Enter'
|
||||
)
|
||||
|
||||
/**
|
||||
* Export.
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import EVENT_HANDLERS from './event-handlers'
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* The transfer types that Slate recognizes.
|
||||
*
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Editor from './components/editor'
|
||||
import cloneFragment from './utils/clone-fragment'
|
||||
import findDOMNode from './utils/find-dom-node'
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Base64 from 'slate-base64-serializer'
|
||||
import Debug from 'debug'
|
||||
import Plain from 'slate-plain-serializer'
|
||||
@@ -225,9 +224,11 @@ function AfterPlugin() {
|
||||
selection.endKey == target.endKey &&
|
||||
selection.endOffset < target.endOffset
|
||||
) {
|
||||
target = target.move(selection.startKey == selection.endKey
|
||||
? 0 - selection.endOffset + selection.startOffset
|
||||
: 0 - selection.endOffset)
|
||||
target = target.move(
|
||||
selection.startKey == selection.endKey
|
||||
? 0 - selection.endOffset + selection.startOffset
|
||||
: 0 - selection.endOffset
|
||||
)
|
||||
}
|
||||
|
||||
if (isDraggingInternally) {
|
||||
@@ -252,12 +253,10 @@ function AfterPlugin() {
|
||||
if (n) change.collapseToStartOf(n)
|
||||
}
|
||||
|
||||
text
|
||||
.split('\n')
|
||||
.forEach((line, i) => {
|
||||
if (i > 0) change.splitBlock()
|
||||
change.insertText(line)
|
||||
})
|
||||
text.split('\n').forEach((line, i) => {
|
||||
if (i > 0) change.splitBlock()
|
||||
change.insertText(line)
|
||||
})
|
||||
}
|
||||
|
||||
if (type == 'fragment') {
|
||||
@@ -280,11 +279,13 @@ function AfterPlugin() {
|
||||
const el = findDOMNode(focusNode, window)
|
||||
if (!el) return
|
||||
|
||||
el.dispatchEvent(new MouseEvent('mouseup', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
}))
|
||||
el.dispatchEvent(
|
||||
new MouseEvent('mouseup', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,11 +317,12 @@ function AfterPlugin() {
|
||||
let start = 0
|
||||
let end = 0
|
||||
|
||||
const leaf = leaves.find((r) => {
|
||||
start = end
|
||||
end += r.text.length
|
||||
if (end >= point.offset) return true
|
||||
}) || lastLeaf
|
||||
const leaf =
|
||||
leaves.find(r => {
|
||||
start = end
|
||||
end += r.text.length
|
||||
if (end >= point.offset) return true
|
||||
}) || lastLeaf
|
||||
|
||||
// Get the text information.
|
||||
const { text } = leaf
|
||||
@@ -342,12 +344,12 @@ function AfterPlugin() {
|
||||
// Determine what the selection should be after changing the text.
|
||||
const delta = textContent.length - text.length
|
||||
const corrected = selection.collapseToEnd().move(delta)
|
||||
const entire = selection.moveAnchorTo(point.key, start).moveFocusTo(point.key, end)
|
||||
const entire = selection
|
||||
.moveAnchorTo(point.key, start)
|
||||
.moveFocusTo(point.key, end)
|
||||
|
||||
// Change the current value to have the leaf's text replaced.
|
||||
change
|
||||
.insertTextAtRange(entire, textContent, leaf.marks)
|
||||
.select(corrected)
|
||||
change.insertTextAtRange(entire, textContent, leaf.marks).select(corrected)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,7 +434,8 @@ function AfterPlugin() {
|
||||
// browsers won't know what to do.
|
||||
if (HOTKEYS.COLLAPSE_CHAR_BACKWARD(event)) {
|
||||
const { document, isInVoid, previousText, startText } = value
|
||||
const isPreviousInVoid = previousText && document.hasVoidParent(previousText.key)
|
||||
const isPreviousInVoid =
|
||||
previousText && document.hasVoidParent(previousText.key)
|
||||
if (isInVoid || isPreviousInVoid || startText.text == '') {
|
||||
event.preventDefault()
|
||||
return change.collapseCharBackward()
|
||||
@@ -450,7 +453,8 @@ function AfterPlugin() {
|
||||
|
||||
if (HOTKEYS.EXTEND_CHAR_BACKWARD(event)) {
|
||||
const { document, isInVoid, previousText, startText } = value
|
||||
const isPreviousInVoid = previousText && document.hasVoidParent(previousText.key)
|
||||
const isPreviousInVoid =
|
||||
previousText && document.hasVoidParent(previousText.key)
|
||||
if (isInVoid || isPreviousInVoid || startText.text == '') {
|
||||
event.preventDefault()
|
||||
return change.extendCharBackward()
|
||||
@@ -493,7 +497,8 @@ function AfterPlugin() {
|
||||
|
||||
const defaultBlock = startBlock
|
||||
const defaultMarks = document.getInsertMarksAtRange(selection)
|
||||
const frag = Plain.deserialize(text, { defaultBlock, defaultMarks }).document
|
||||
const frag = Plain.deserialize(text, { defaultBlock, defaultMarks })
|
||||
.document
|
||||
change.insertFragment(frag)
|
||||
}
|
||||
}
|
||||
@@ -621,7 +626,11 @@ function AfterPlugin() {
|
||||
if (node.object != 'block' && node.object != 'inline') return
|
||||
const Tag = node.object == 'block' ? 'div' : 'span'
|
||||
const style = { position: 'relative' }
|
||||
return <Tag {...attributes} style={style}>{children}</Tag>
|
||||
return (
|
||||
<Tag {...attributes} style={style}>
|
||||
{children}
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
import getWindow from 'get-window'
|
||||
import { findDOMNode } from 'react-dom'
|
||||
@@ -8,7 +7,7 @@ import {
|
||||
IS_FIREFOX,
|
||||
IS_IOS,
|
||||
IS_ANDROID,
|
||||
SUPPORTED_EVENTS
|
||||
SUPPORTED_EVENTS,
|
||||
} from '../constants/environment'
|
||||
import findNode from '../utils/find-node'
|
||||
|
||||
@@ -117,9 +116,7 @@ function BeforePlugin() {
|
||||
// happen on the initialization of the editor, or if the schema changes.
|
||||
// This change isn't save into history since only schema is updated.
|
||||
if (value.schema != editor.schema) {
|
||||
change
|
||||
.setValue({ schema: editor.schema }, { save: false })
|
||||
.normalize()
|
||||
change.setValue({ schema: editor.schema }, { save: false }).normalize()
|
||||
}
|
||||
|
||||
debug('onChange')
|
||||
@@ -183,7 +180,7 @@ function BeforePlugin() {
|
||||
function onCopy(event, change, editor) {
|
||||
const window = getWindow(event.target)
|
||||
isCopying = true
|
||||
window.requestAnimationFrame(() => isCopying = false)
|
||||
window.requestAnimationFrame(() => (isCopying = false))
|
||||
|
||||
debug('onCopy', { event })
|
||||
}
|
||||
@@ -201,7 +198,7 @@ function BeforePlugin() {
|
||||
|
||||
const window = getWindow(event.target)
|
||||
isCopying = true
|
||||
window.requestAnimationFrame(() => isCopying = false)
|
||||
window.requestAnimationFrame(() => (isCopying = false))
|
||||
|
||||
debug('onCut', { event })
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Base64 from 'slate-base64-serializer'
|
||||
|
||||
import findDOMNode from './find-dom-node'
|
||||
@@ -48,10 +47,10 @@ function cloneFragment(event, value, fragment = value.fragment) {
|
||||
// check if there is marks involved. If so, set the range start just before the
|
||||
// startText node
|
||||
if ((IS_CHROME || IS_SAFARI) && !isVoid && startKey === endKey) {
|
||||
const hasMarks = startText.characters
|
||||
.slice(value.selection.anchorOffset, value.selection.focusOffset)
|
||||
.filter(char => char.marks.size !== 0)
|
||||
.size !== 0
|
||||
const hasMarks =
|
||||
startText.characters
|
||||
.slice(value.selection.anchorOffset, value.selection.focusOffset)
|
||||
.filter(char => char.marks.size !== 0).size !== 0
|
||||
if (hasMarks) {
|
||||
const r = range.cloneRange()
|
||||
const node = findDOMNode(startText, window)
|
||||
@@ -63,14 +62,18 @@ function cloneFragment(event, value, fragment = value.fragment) {
|
||||
|
||||
// Remove any zero-width space spans from the cloned DOM so that they don't
|
||||
// show up elsewhere when pasted.
|
||||
const zws = [].slice.call(contents.querySelectorAll('[data-slate-zero-width]'))
|
||||
const zws = [].slice.call(
|
||||
contents.querySelectorAll('[data-slate-zero-width]')
|
||||
)
|
||||
zws.forEach(zw => zw.parentNode.removeChild(zw))
|
||||
|
||||
// COMPAT: In Chrome and Safari, if the last element in the selection to
|
||||
// copy has `contenteditable="false"` the copy will fail, and nothing will
|
||||
// be put in the clipboard. So we remove them all. (2017/05/04)
|
||||
if (IS_CHROME || IS_SAFARI) {
|
||||
const els = [].slice.call(contents.querySelectorAll('[contenteditable="false"]'))
|
||||
const els = [].slice.call(
|
||||
contents.querySelectorAll('[contenteditable="false"]')
|
||||
)
|
||||
els.forEach(el => el.removeAttribute('contenteditable'))
|
||||
}
|
||||
|
||||
@@ -104,7 +107,8 @@ function cloneFragment(event, value, fragment = value.fragment) {
|
||||
div.style.border = '0px'
|
||||
div.style.padding = '0px'
|
||||
div.style.margin = '0px'
|
||||
div.style.top = `${window.pageYOffset || window.document.documentElement.scrollTop}px`
|
||||
div.style.top = `${window.pageYOffset ||
|
||||
window.document.documentElement.scrollTop}px`
|
||||
|
||||
div.appendChild(contents)
|
||||
editor.appendChild(div)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Find the deepest descendant of a DOM `element`.
|
||||
*
|
||||
@@ -7,9 +6,7 @@
|
||||
*/
|
||||
|
||||
function findDeepestNode(element) {
|
||||
return element.firstChild
|
||||
? findDeepestNode(element.firstChild)
|
||||
: element
|
||||
return element.firstChild ? findDeepestNode(element.firstChild) : element
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { Node } from 'slate'
|
||||
|
||||
/**
|
||||
@@ -17,7 +16,9 @@ function findDOMNode(key, win = window) {
|
||||
const el = win.document.querySelector(`[data-key="${key}"]`)
|
||||
|
||||
if (!el) {
|
||||
throw new Error(`Unable to find a DOM node for "${key}". This is often because of forgetting to add \`props.attributes\` to a custom component.`)
|
||||
throw new Error(
|
||||
`Unable to find a DOM node for "${key}". This is often because of forgetting to add \`props.attributes\` to a custom component.`
|
||||
)
|
||||
}
|
||||
|
||||
return el
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import findDOMNode from './find-dom-node'
|
||||
|
||||
/**
|
||||
@@ -24,7 +23,7 @@ function findDOMPoint(key, offset, win = window) {
|
||||
false
|
||||
)
|
||||
|
||||
while (n = iterator.nextNode()) {
|
||||
while ((n = iterator.nextNode())) {
|
||||
const { length } = n.textContent
|
||||
const end = start + length
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import findDOMPoint from './find-dom-point'
|
||||
|
||||
/**
|
||||
@@ -10,7 +9,14 @@ import findDOMPoint from './find-dom-point'
|
||||
*/
|
||||
|
||||
function findDOMRange(range, win = window) {
|
||||
const { anchorKey, anchorOffset, focusKey, focusOffset, isBackward, isCollapsed } = range
|
||||
const {
|
||||
anchorKey,
|
||||
anchorOffset,
|
||||
focusKey,
|
||||
focusOffset,
|
||||
isBackward,
|
||||
isCollapsed,
|
||||
} = range
|
||||
const anchor = findDOMPoint(anchorKey, anchorOffset, win)
|
||||
const focus = isCollapsed ? anchor : findDOMPoint(focusKey, focusOffset, win)
|
||||
if (!anchor || !focus) return null
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Find a Slate node from a DOM `element`.
|
||||
*
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import getWindow from 'get-window'
|
||||
|
||||
import OffsetKey from './offset-key'
|
||||
@@ -24,10 +23,10 @@ const VOID_SELECTOR = '[data-slate-void]'
|
||||
*/
|
||||
|
||||
function findPoint(nativeNode, nativeOffset, value) {
|
||||
const {
|
||||
node: nearestNode,
|
||||
offset: nearestOffset,
|
||||
} = normalizeNodeAndOffset(nativeNode, nativeOffset)
|
||||
const { node: nearestNode, offset: nearestOffset } = normalizeNodeAndOffset(
|
||||
nativeNode,
|
||||
nativeOffset
|
||||
)
|
||||
|
||||
const window = getWindow(nativeNode)
|
||||
const { parentNode } = nearestNode
|
||||
@@ -44,11 +43,9 @@ function findPoint(nativeNode, nativeOffset, value) {
|
||||
range.setEnd(nearestNode, nearestOffset)
|
||||
node = textNode
|
||||
offset = range.toString().length
|
||||
}
|
||||
|
||||
// For void nodes, the element with the offset key will be a cousin, not an
|
||||
// ancestor, so find it by going down from the nearest void parent.
|
||||
else {
|
||||
} else {
|
||||
// For void nodes, the element with the offset key will be a cousin, not an
|
||||
// ancestor, so find it by going down from the nearest void parent.
|
||||
const voidNode = parentNode.closest(VOID_SELECTOR)
|
||||
if (!voidNode) return null
|
||||
rangeNode = voidNode.querySelector(RANGE_SELECTOR)
|
||||
@@ -138,7 +135,7 @@ function getEditableChild(parent, index, direction) {
|
||||
// While the child is a comment node, or an element node with no children,
|
||||
// keep iterating to find a sibling non-void, non-comment node.
|
||||
while (
|
||||
(child.nodeType == 8) ||
|
||||
child.nodeType == 8 ||
|
||||
(child.nodeType == 1 && child.childNodes.length == 0) ||
|
||||
(child.nodeType == 1 && child.getAttribute('contenteditable') == 'false')
|
||||
) {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import getWindow from 'get-window'
|
||||
import isBackward from 'selection-is-backward'
|
||||
import { Range } from 'slate'
|
||||
@@ -23,7 +22,10 @@ function findRange(native, value) {
|
||||
|
||||
// If the `native` object is a DOM `Range` or `StaticRange` object, change it
|
||||
// into something that looks like a DOM `Selection` instead.
|
||||
if (native instanceof window.Range || (window.StaticRange && native instanceof window.StaticRange)) {
|
||||
if (
|
||||
native instanceof window.Range ||
|
||||
(window.StaticRange && native instanceof window.StaticRange)
|
||||
) {
|
||||
native = {
|
||||
anchorNode: native.startContainer,
|
||||
anchorOffset: native.startOffset,
|
||||
@@ -32,7 +34,13 @@ function findRange(native, value) {
|
||||
}
|
||||
}
|
||||
|
||||
const { anchorNode, anchorOffset, focusNode, focusOffset, isCollapsed } = native
|
||||
const {
|
||||
anchorNode,
|
||||
anchorOffset,
|
||||
focusNode,
|
||||
focusOffset,
|
||||
isCollapsed,
|
||||
} = native
|
||||
const anchor = findPoint(anchorNode, anchorOffset, value)
|
||||
const focus = isCollapsed ? anchor : findPoint(focusNode, focusOffset, value)
|
||||
if (!anchor || !focus) return null
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import getWindow from 'get-window'
|
||||
|
||||
import { Range } from 'slate'
|
||||
@@ -30,9 +29,10 @@ function getEventRange(event, value) {
|
||||
// closest to.
|
||||
if (node.isVoid) {
|
||||
const rect = target.getBoundingClientRect()
|
||||
const isPrevious = node.object == 'inline'
|
||||
? x - rect.left < rect.left + rect.width - x
|
||||
: y - rect.top < rect.top + rect.height - y
|
||||
const isPrevious =
|
||||
node.object == 'inline'
|
||||
? x - rect.left < rect.left + rect.width - x
|
||||
: y - rect.top < rect.top + rect.height - y
|
||||
|
||||
const text = node.getFirstText()
|
||||
const range = Range.create()
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import Base64 from 'slate-base64-serializer'
|
||||
|
||||
import TRANSFER_TYPES from '../constants/transfer-types'
|
||||
@@ -9,13 +8,7 @@ import TRANSFER_TYPES from '../constants/transfer-types'
|
||||
* @type {String}
|
||||
*/
|
||||
|
||||
const {
|
||||
FRAGMENT,
|
||||
HTML,
|
||||
NODE,
|
||||
RICH,
|
||||
TEXT
|
||||
} = TRANSFER_TYPES
|
||||
const { FRAGMENT, HTML, NODE, RICH, TEXT } = TRANSFER_TYPES
|
||||
|
||||
/**
|
||||
* Fragment matching regexp for HTML nodes.
|
||||
@@ -47,13 +40,9 @@ function getEventTransfer(event) {
|
||||
|
||||
// If there isn't a fragment, but there is HTML, check to see if the HTML is
|
||||
// actually an encoded fragment.
|
||||
if (
|
||||
!fragment &&
|
||||
html &&
|
||||
~html.indexOf(' data-slate-fragment="')
|
||||
) {
|
||||
if (!fragment && html && ~html.indexOf(' data-slate-fragment="')) {
|
||||
const matches = FRAGMENT_MATCHER.exec(html)
|
||||
const [ full, encoded ] = matches // eslint-disable-line no-unused-vars
|
||||
const [full, encoded] = matches // eslint-disable-line no-unused-vars
|
||||
if (encoded) fragment = encoded
|
||||
}
|
||||
|
||||
@@ -77,7 +66,7 @@ function getEventTransfer(event) {
|
||||
// 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)
|
||||
.map(item => (item.kind == 'file' ? item.getAsFile() : null))
|
||||
.filter(exists => exists)
|
||||
} else if (transfer.files && transfer.files.length) {
|
||||
files = Array.from(transfer.files)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { findDOMNode } from 'react-dom'
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Noop.
|
||||
*
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Offset key parser regex.
|
||||
*
|
||||
@@ -21,10 +20,10 @@ function parse(string) {
|
||||
throw new Error(`Invalid offset key string "${string}".`)
|
||||
}
|
||||
|
||||
const [ original, key, index ] = matches // eslint-disable-line no-unused-vars
|
||||
const [original, key, index] = matches // eslint-disable-line no-unused-vars
|
||||
return {
|
||||
key,
|
||||
index: parseInt(index, 10)
|
||||
index: parseInt(index, 10),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,5 +48,5 @@ function stringify(object) {
|
||||
|
||||
export default {
|
||||
parse,
|
||||
stringify
|
||||
stringify,
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import getWindow from 'get-window'
|
||||
import isBackward from 'selection-is-backward'
|
||||
import { IS_SAFARI, IS_IOS } from '../constants/environment'
|
||||
@@ -9,11 +8,7 @@ import { IS_SAFARI, IS_IOS } from '../constants/environment'
|
||||
* @type {Array}
|
||||
*/
|
||||
|
||||
const OVERFLOWS = [
|
||||
'auto',
|
||||
'overlay',
|
||||
'scroll',
|
||||
]
|
||||
const OVERFLOWS = ['auto', 'overlay', 'scroll']
|
||||
|
||||
/**
|
||||
* Detect whether we are running IOS version 11
|
||||
@@ -69,7 +64,9 @@ function scrollToSelection(selection) {
|
||||
|
||||
const window = getWindow(selection.anchorNode)
|
||||
const scroller = findScrollContainer(selection.anchorNode, window)
|
||||
const isWindow = scroller == window.document.body || scroller == window.document.documentElement
|
||||
const isWindow =
|
||||
scroller == window.document.body ||
|
||||
scroller == window.document.documentElement
|
||||
const backward = isBackward(selection)
|
||||
|
||||
const range = selection.getRangeAt(0).cloneRange()
|
||||
@@ -136,8 +133,10 @@ function scrollToSelection(selection) {
|
||||
height = offsetHeight
|
||||
scrollerTop = scrollerRect.top + parseInt(borderTopWidth, 10)
|
||||
scrollerLeft = scrollerRect.left + parseInt(borderLeftWidth, 10)
|
||||
scrollerBordersY = parseInt(borderTopWidth, 10) + parseInt(borderBottomWidth, 10)
|
||||
scrollerBordersX = parseInt(borderLeftWidth, 10) + parseInt(borderRightWidth, 10)
|
||||
scrollerBordersY =
|
||||
parseInt(borderTopWidth, 10) + parseInt(borderBottomWidth, 10)
|
||||
scrollerBordersX =
|
||||
parseInt(borderLeftWidth, 10) + parseInt(borderRightWidth, 10)
|
||||
scrollerPaddingTop = parseInt(paddingTop, 10)
|
||||
scrollerPaddingBottom = parseInt(paddingBottom, 10)
|
||||
scrollerPaddingLeft = parseInt(paddingLeft, 10)
|
||||
@@ -155,7 +154,10 @@ function scrollToSelection(selection) {
|
||||
if (cursorLeft < xOffset) {
|
||||
// selection to the left of viewport
|
||||
x = cursorLeft - scrollerPaddingLeft
|
||||
} else if (cursorLeft + cursorRect.width + scrollerBordersX > xOffset + width) {
|
||||
} else if (
|
||||
cursorLeft + cursorRect.width + scrollerBordersX >
|
||||
xOffset + width
|
||||
) {
|
||||
// selection to the right of viewport
|
||||
x = cursorLeft + scrollerBordersX + scrollerPaddingRight - width
|
||||
}
|
||||
@@ -163,12 +165,19 @@ function scrollToSelection(selection) {
|
||||
if (cursorTop < yOffset) {
|
||||
// selection above viewport
|
||||
y = cursorTop - scrollerPaddingTop
|
||||
} else if (cursorTop + cursorRect.height + scrollerBordersY > yOffset + height) {
|
||||
} else if (
|
||||
cursorTop + cursorRect.height + scrollerBordersY >
|
||||
yOffset + height
|
||||
) {
|
||||
// selection below viewport
|
||||
y = cursorTop + scrollerBordersY + scrollerPaddingBottom + cursorRect.height - height
|
||||
y =
|
||||
cursorTop +
|
||||
scrollerBordersY +
|
||||
scrollerPaddingBottom +
|
||||
cursorRect.height -
|
||||
height
|
||||
}
|
||||
|
||||
|
||||
if (isWindow) {
|
||||
window.scrollTo(x, y)
|
||||
} else {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import TRANSFER_TYPES from '../constants/transfer-types'
|
||||
|
||||
/**
|
||||
@@ -45,12 +44,12 @@ function setEventTransfer(event, type, content) {
|
||||
try {
|
||||
obj = JSON.parse(text.substring(prefix.length))
|
||||
} catch (e) {
|
||||
throw new Error('Failed to parse Slate data from `DataTransfer` object.')
|
||||
throw new Error(
|
||||
'Failed to parse Slate data from `DataTransfer` object.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, it's just set it as is.
|
||||
else {
|
||||
} else {
|
||||
// Otherwise, it's just set it as is.
|
||||
obj[TEXT] = text
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,12 @@
|
||||
|
||||
import { JSDOM } from 'jsdom' // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const UNWANTED_ATTRS = [
|
||||
'data-key',
|
||||
'data-offset-key'
|
||||
]
|
||||
const UNWANTED_ATTRS = ['data-key', 'data-offset-key']
|
||||
|
||||
const UNWANTED_TOP_LEVEL_ATTRS = [
|
||||
'autocorrect',
|
||||
'spellcheck',
|
||||
'style',
|
||||
'data-gramm'
|
||||
'data-gramm',
|
||||
]
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { createHyperscript } from 'slate-hyperscript'
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,7 @@ const h = createHyperscript({
|
||||
image: {
|
||||
type: 'image',
|
||||
isVoid: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
inlines: {
|
||||
link: 'link',
|
||||
@@ -25,7 +24,7 @@ const h = createHyperscript({
|
||||
emoji: {
|
||||
type: 'emoji',
|
||||
isVoid: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
marks: {
|
||||
b: 'bold',
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
import h from '../../../helpers/h'
|
||||
|
||||
export default function (simulator) {
|
||||
export default function(simulator) {
|
||||
simulator.blur()
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
import h from '../../../helpers/h'
|
||||
|
||||
export default function (simulator) {
|
||||
export default function(simulator) {
|
||||
simulator.keyDown({ key: 'Enter' })
|
||||
}
|
||||
|
||||
|
@@ -3,10 +3,13 @@
|
||||
import h from '../../../helpers/h'
|
||||
import { Range } from 'slate'
|
||||
|
||||
export default function (simulator) {
|
||||
export default function(simulator) {
|
||||
const { value } = simulator
|
||||
const text = value.document.getTexts().first()
|
||||
const selection = Range.create().collapseToStartOf(text).move(1).focus()
|
||||
const selection = Range.create()
|
||||
.collapseToStartOf(text)
|
||||
.move(1)
|
||||
.focus()
|
||||
simulator.select(null, { selection })
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user