mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-21 13:51:59 +02:00
fixes
This commit is contained in:
parent
dbcb9e531f
commit
ccac6102a5
examples
auto-markdown
code-highlighting
development/performance-rich
embeds
hovering-menu
iframes
images
index.csslinks
paste-html
rich-text
rtl
tables
lib
package.jsontest
rendering/fixtures
custom-block-void
custom-block
custom-decorator
custom-inline
custom-mark-multiple
custom-mark-with-component
custom-mark-with-function
custom-mark-with-mixed
custom-mark-with-object
custom-mark-with-string
multiple-custom-block
multiple-custom-inline
schema
@ -4,21 +4,23 @@ import React from 'react'
|
||||
import initialState from './state.json'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'block-quote': props => <blockquote>{props.children}</blockquote>,
|
||||
'bulleted-list': props => <ul>{props.children}</ul>,
|
||||
'heading-one': props => <h1>{props.children}</h1>,
|
||||
'heading-two': props => <h2>{props.children}</h2>,
|
||||
'heading-three': props => <h3>{props.children}</h3>,
|
||||
'heading-four': props => <h4>{props.children}</h4>,
|
||||
'heading-five': props => <h5>{props.children}</h5>,
|
||||
'heading-six': props => <h6>{props.children}</h6>,
|
||||
'list-item': props => <li>{props.children}</li>
|
||||
const schema = {
|
||||
nodes: {
|
||||
'block-quote': props => <blockquote>{props.children}</blockquote>,
|
||||
'bulleted-list': props => <ul>{props.children}</ul>,
|
||||
'heading-one': props => <h1>{props.children}</h1>,
|
||||
'heading-two': props => <h2>{props.children}</h2>,
|
||||
'heading-three': props => <h3>{props.children}</h3>,
|
||||
'heading-four': props => <h4>{props.children}</h4>,
|
||||
'heading-five': props => <h5>{props.children}</h5>,
|
||||
'heading-six': props => <h6>{props.children}</h6>,
|
||||
'list-item': props => <li>{props.children}</li>,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,26 +75,15 @@ class AutoMarkdown extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
renderNode={this.renderNode}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Element}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* On change.
|
||||
*
|
||||
|
@ -47,6 +47,44 @@ function CodeBlock(props) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a Prism.js decorator for code blocks.
|
||||
*
|
||||
* @param {Text} text
|
||||
* @param {Block} block
|
||||
*/
|
||||
|
||||
function codeBlockDecorator(text, block) {
|
||||
let characters = text.characters.asMutable()
|
||||
const language = block.data.get('language')
|
||||
const string = text.text
|
||||
const grammar = Prism.languages[language]
|
||||
const tokens = Prism.tokenize(string, grammar)
|
||||
let offset = 0
|
||||
|
||||
for (const token of tokens) {
|
||||
if (typeof token == 'string') {
|
||||
offset += token.length
|
||||
continue
|
||||
}
|
||||
|
||||
const length = offset + token.content.length
|
||||
const type = `highlight-${token.type}`
|
||||
|
||||
for (let i = offset; i < length; i++) {
|
||||
let char = characters.get(i)
|
||||
let { marks } = char
|
||||
marks = marks.add(Mark.create({ type }))
|
||||
char = char.merge({ marks })
|
||||
characters = characters.set(i, char)
|
||||
}
|
||||
|
||||
offset = length
|
||||
}
|
||||
|
||||
return characters.asImmutable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a schema.
|
||||
*
|
||||
@ -57,36 +95,7 @@ const schema = {
|
||||
nodes: {
|
||||
code: {
|
||||
component: CodeBlock,
|
||||
decorator: (block, text) => {
|
||||
let characters = text.characters.asMutable()
|
||||
const language = block.data.get('language')
|
||||
const string = text.text
|
||||
const grammar = Prism.languages[language]
|
||||
const tokens = Prism.tokenize(string, grammar)
|
||||
let offset = 0
|
||||
|
||||
for (const token of tokens) {
|
||||
if (typeof token == 'string') {
|
||||
offset += token.length
|
||||
continue
|
||||
}
|
||||
|
||||
const length = offset + token.content.length
|
||||
const type = `highlight-${token.type}`
|
||||
|
||||
for (let i = offset; i < length; i++) {
|
||||
let char = characters.get(i)
|
||||
let { marks } = char
|
||||
marks = marks.add(Mark.create({ type }))
|
||||
char = char.merge({ marks })
|
||||
characters = characters.set(i, char)
|
||||
}
|
||||
|
||||
offset = length
|
||||
}
|
||||
|
||||
return characters.asImmutable()
|
||||
}
|
||||
decorator: codeBlockDecorator,
|
||||
}
|
||||
},
|
||||
marks: {
|
||||
|
@ -10,41 +10,36 @@ import initialState from './state.json'
|
||||
const DEFAULT_NODE = 'paragraph'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'block-quote': props => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
'bulleted-list': props => <ul {...props.attributes}>{props.children}</ul>,
|
||||
'heading-one': props => <h1 {...props.attributes}>{props.children}</h1>,
|
||||
'heading-two': props => <h2 {...props.attributes}>{props.children}</h2>,
|
||||
'list-item': props => <li {...props.attributes}>{props.children}</li>,
|
||||
'numbered-list': props => <ol {...props.attributes}>{props.children}</ol>
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a set of mark renderers.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const MARKS = {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
const schema = {
|
||||
nodes: {
|
||||
'block-quote': props => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
'bulleted-list': props => <ul {...props.attributes}>{props.children}</ul>,
|
||||
'heading-one': props => <h1 {...props.attributes}>{props.children}</h1>,
|
||||
'heading-two': props => <h2 {...props.attributes}>{props.children}</h2>,
|
||||
'list-item': props => <li {...props.attributes}>{props.children}</li>,
|
||||
'numbered-list': props => <ol {...props.attributes}>{props.children}</ol>
|
||||
},
|
||||
code: {
|
||||
fontFamily: 'monospace',
|
||||
backgroundColor: '#eee',
|
||||
padding: '3px',
|
||||
borderRadius: '4px'
|
||||
},
|
||||
italic: {
|
||||
fontStyle: 'italic'
|
||||
},
|
||||
underlined: {
|
||||
textDecoration: 'underline'
|
||||
marks: {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
code: {
|
||||
fontFamily: 'monospace',
|
||||
backgroundColor: '#eee',
|
||||
padding: '3px',
|
||||
borderRadius: '4px'
|
||||
},
|
||||
italic: {
|
||||
fontStyle: 'italic'
|
||||
},
|
||||
underlined: {
|
||||
textDecoration: 'underline'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,9 +278,8 @@ class RichText extends React.Component {
|
||||
<div className="editor">
|
||||
<Editor
|
||||
placeholder={'Enter some rich text...'}
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
renderMark={this.renderMark}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
@ -293,28 +287,6 @@ class RichText extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node renderer for a Slate `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Component or Void}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mark renderer for a Slate `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object or Void}
|
||||
*/
|
||||
|
||||
renderMark = (mark) => {
|
||||
return MARKS[mark.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,13 +6,15 @@ import Video from './video'
|
||||
import initialState from './state.json'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
video: Video
|
||||
const schema = {
|
||||
nodes: {
|
||||
video: Video
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,25 +55,14 @@ class Embeds extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Element}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,26 +6,17 @@ import position from 'selection-position'
|
||||
import initialState from './state.json'
|
||||
|
||||
/**
|
||||
* Define a set of mark renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const MARKS = {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
code: {
|
||||
fontFamily: 'monospace',
|
||||
backgroundColor: '#eee',
|
||||
padding: '3px',
|
||||
borderRadius: '4px'
|
||||
},
|
||||
italic: {
|
||||
fontStyle: 'italic'
|
||||
},
|
||||
underlined: {
|
||||
textDecoration: 'underline'
|
||||
const schema = {
|
||||
marks: {
|
||||
bold: props => <strong>{props.children}</strong>,
|
||||
code: props => <code>{props.children}</code>,
|
||||
italic: props => <em>{props.children}</em>,
|
||||
underlined: props => <u>{props.children}</u>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,25 +166,14 @@ class HoveringMenu extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderMark={this.renderMark}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mark renderer for a Slate `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object or Void}
|
||||
*/
|
||||
|
||||
renderMark = (mark) => {
|
||||
return MARKS[mark.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the menu's absolute position.
|
||||
*/
|
||||
|
@ -20,28 +20,23 @@ injector()
|
||||
const DEFAULT_NODE = 'paragraph'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'block-code': props => <pre><code {...props.attributes}>{props.children}</code></pre>,
|
||||
'block-quote': props => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
'heading-two': props => <h2 {...props.attributes}>{props.children}</h2>,
|
||||
'paragraph': props => <p {...props.attributes}>{props.children}</p>
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a set of mark renderers.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const MARKS = {
|
||||
bold: props => <strong>{props.children}</strong>,
|
||||
highlight: props => <mark>{props.children}</mark>,
|
||||
italic: props => <em>{props.children}</em>,
|
||||
const schema = {
|
||||
nodes: {
|
||||
'block-code': props => <pre><code {...props.attributes}>{props.children}</code></pre>,
|
||||
'block-quote': props => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
'heading-two': props => <h2 {...props.attributes}>{props.children}</h2>,
|
||||
'paragraph': props => <p {...props.attributes}>{props.children}</p>,
|
||||
},
|
||||
marks: {
|
||||
bold: props => <strong>{props.children}</strong>,
|
||||
highlight: props => <mark>{props.children}</mark>,
|
||||
italic: props => <em>{props.children}</em>,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -274,37 +269,14 @@ class Iframes extends React.Component {
|
||||
return (
|
||||
<Editor
|
||||
placeholder={'Enter some rich text...'}
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
renderMark={this.renderMark}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node renderer for a Slate `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Component or Void}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mark renderer for a Slate `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object or Void}
|
||||
*/
|
||||
|
||||
renderMark = (mark) => {
|
||||
return MARKS[mark.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Iframes
|
||||
|
@ -7,20 +7,22 @@ import isImage from 'is-image'
|
||||
import isUrl from 'is-url'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
image: (props) => {
|
||||
const { node, state } = props
|
||||
const isFocused = state.selection.hasEdgeIn(node)
|
||||
const src = node.data.get('src')
|
||||
const className = isFocused ? 'active' : null
|
||||
return (
|
||||
<img src={src} className={className} {...props.attributes} />
|
||||
)
|
||||
const schema = {
|
||||
nodes: {
|
||||
image: (props) => {
|
||||
const { node, state } = props
|
||||
const isFocused = state.selection.hasEdgeIn(node)
|
||||
const src = node.data.get('src')
|
||||
const className = isFocused ? 'active' : null
|
||||
return (
|
||||
<img src={src} className={className} {...props.attributes} />
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,8 +85,8 @@ class Images extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
onChange={this.onChange}
|
||||
onDocumentChange={this.onDocumentChange}
|
||||
onDrop={this.onDrop}
|
||||
@ -94,17 +96,6 @@ class Images extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Element}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* On change.
|
||||
*
|
||||
|
@ -21,6 +21,11 @@ pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
:not(pre) > code {
|
||||
background-color: #eee;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 20em;
|
||||
|
@ -7,17 +7,19 @@ import isUrl from 'is-url'
|
||||
import { Map } from 'immutable'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
paragraph: props => <p>{props.children}</p>,
|
||||
link: (props) => {
|
||||
const { data } = props.node
|
||||
const href = data.get('href')
|
||||
return <a {...props.attributes} href={href}>{props.children}</a>
|
||||
const schema = {
|
||||
nodes: {
|
||||
paragraph: props => <p>{props.children}</p>,
|
||||
link: (props) => {
|
||||
const { data } = props.node
|
||||
const href = data.get('href')
|
||||
return <a {...props.attributes} href={href}>{props.children}</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,8 +183,8 @@ class Links extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
onChange={this.onChange}
|
||||
onPaste={this.onPaste}
|
||||
/>
|
||||
@ -190,17 +192,6 @@ class Links extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Element}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,51 +4,35 @@ import React from 'react'
|
||||
import initialState from './state.json'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'bulleted-list': props => <ul {...props.attributes}>{props.children}</ul>,
|
||||
'code': props => <pre><code {...props.attributes}>{props.children}</code></pre>,
|
||||
'heading-one': props => <h1 {...props.attributes}>{props.children}</h1>,
|
||||
'heading-two': props => <h2 {...props.attributes}>{props.children}</h2>,
|
||||
'heading-three': props => <h3 {...props.attributes}>{props.children}</h3>,
|
||||
'heading-four': props => <h4 {...props.attributes}>{props.children}</h4>,
|
||||
'heading-five': props => <h5 {...props.attributes}>{props.children}</h5>,
|
||||
'heading-six': props => <h6 {...props.attributes}>{props.children}</h6>,
|
||||
'list-item': props => <li {...props.attributes}>{props.children}</li>,
|
||||
'numbered-list': props => <ol {...props.attributes}>{props.children}</ol>,
|
||||
'quote': props => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
'link': (props) => {
|
||||
const { data } = props.node
|
||||
const href = data.get('href')
|
||||
return <a href={href} {...props.attributes}>{props.children}</a>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a set of mark renderers.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const MARKS = {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
const schema = {
|
||||
nodes: {
|
||||
'bulleted-list': props => <ul {...props.attributes}>{props.children}</ul>,
|
||||
'code': props => <pre><code {...props.attributes}>{props.children}</code></pre>,
|
||||
'heading-one': props => <h1 {...props.attributes}>{props.children}</h1>,
|
||||
'heading-two': props => <h2 {...props.attributes}>{props.children}</h2>,
|
||||
'heading-three': props => <h3 {...props.attributes}>{props.children}</h3>,
|
||||
'heading-four': props => <h4 {...props.attributes}>{props.children}</h4>,
|
||||
'heading-five': props => <h5 {...props.attributes}>{props.children}</h5>,
|
||||
'heading-six': props => <h6 {...props.attributes}>{props.children}</h6>,
|
||||
'list-item': props => <li {...props.attributes}>{props.children}</li>,
|
||||
'numbered-list': props => <ol {...props.attributes}>{props.children}</ol>,
|
||||
'quote': props => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
'link': (props) => {
|
||||
const { data } = props.node
|
||||
const href = data.get('href')
|
||||
return <a href={href} {...props.attributes}>{props.children}</a>
|
||||
}
|
||||
},
|
||||
code: {
|
||||
fontFamily: 'monospace',
|
||||
backgroundColor: '#eee',
|
||||
padding: '3px',
|
||||
borderRadius: '4px'
|
||||
},
|
||||
italic: {
|
||||
fontStyle: 'italic'
|
||||
},
|
||||
underlined: {
|
||||
textDecoration: 'underline'
|
||||
marks: {
|
||||
bold: props => <strong>{props.children}</strong>,
|
||||
code: props => <code>{props.children}</code>,
|
||||
italic: props => <em>{props.children}</em>,
|
||||
underlined: props => <u>{props.children}</u>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,9 +196,8 @@ class PasteHtml extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
renderMark={this.renderMark}
|
||||
onPaste={this.onPaste}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
@ -222,28 +205,6 @@ class PasteHtml extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node renderer for a Slate `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Component or Void}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mark renderer for a Slate `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object or Void}
|
||||
*/
|
||||
|
||||
renderMark = (mark) => {
|
||||
return MARKS[mark.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -299,7 +299,6 @@ class RichText extends React.Component {
|
||||
placeholder={'Enter some rich text...'}
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderMark={this.renderMark}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
@ -307,17 +306,6 @@ class RichText extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mark renderer for a Slate `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object or Void}
|
||||
*/
|
||||
|
||||
renderMark = (mark) => {
|
||||
return MARKS[mark.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,13 +5,15 @@ import SoftBreak from 'slate-soft-break'
|
||||
import initialState from './state.json'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'block-quote': (props) => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
const schema = {
|
||||
nodes: {
|
||||
'block-quote': (props) => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,25 +71,14 @@ class PlainText extends React.Component {
|
||||
return (
|
||||
<Editor
|
||||
placeholder={'Enter some plain text...'}
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
renderNode={this.renderNode}
|
||||
state={this.state.state}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node renderer for a Slate `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Component or Void}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,26 +5,19 @@ import initialState from './state.json'
|
||||
import keycode from 'keycode'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
* Define a schema.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'table': props => <table><tbody {...props.attributes}>{props.children}</tbody></table>,
|
||||
'table-row': props => <tr {...props.attributes}>{props.children}</tr>,
|
||||
'table-cell': props => <td {...props.attributes}>{props.children}</td>
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a set of mark renderers.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const MARKS = {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
const schema = {
|
||||
nodes: {
|
||||
'table': props => <table><tbody {...props.attributes}>{props.children}</tbody></table>,
|
||||
'table-row': props => <tr {...props.attributes}>{props.children}</tr>,
|
||||
'table-cell': props => <td {...props.attributes}>{props.children}</td>,
|
||||
},
|
||||
marks: {
|
||||
'bold': props => <strong>{props.children}</strong>
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,9 +118,8 @@ class Tables extends React.Component {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
schema={schema}
|
||||
state={this.state.state}
|
||||
renderNode={this.renderNode}
|
||||
renderMark={this.renderMark}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
@ -135,28 +127,6 @@ class Tables extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node renderer for a Slate `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Component or Void}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mark renderer for a Slate `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object or Void}
|
||||
*/
|
||||
|
||||
renderMark = (mark) => {
|
||||
return MARKS[mark.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +54,7 @@ class Editor extends React.Component {
|
||||
static propTypes = {
|
||||
className: React.PropTypes.string,
|
||||
onBeforeChange: React.PropTypes.func,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
onChange: React.PropTypes.func,
|
||||
onDocumentChange: React.PropTypes.func,
|
||||
onSelectionChange: React.PropTypes.func,
|
||||
placeholder: React.PropTypes.any,
|
||||
@ -73,6 +73,7 @@ class Editor extends React.Component {
|
||||
*/
|
||||
|
||||
static defaultProps = {
|
||||
onChange: noop,
|
||||
onDocumentChange: noop,
|
||||
onSelectionChange: noop,
|
||||
plugins: [],
|
||||
|
@ -31,7 +31,8 @@ class Void extends React.Component {
|
||||
children: React.PropTypes.any.isRequired,
|
||||
editor: React.PropTypes.object.isRequired,
|
||||
node: React.PropTypes.object.isRequired,
|
||||
state: React.PropTypes.object.isRequired
|
||||
schema: React.PropTypes.object.isRequired,
|
||||
state: React.PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -117,7 +118,7 @@ class Void extends React.Component {
|
||||
*/
|
||||
|
||||
renderLeaf = () => {
|
||||
const { node, state } = this.props
|
||||
const { node, schema, state } = this.props
|
||||
const child = node.getTexts().first()
|
||||
const ranges = child.getRanges()
|
||||
const text = ''
|
||||
@ -133,6 +134,7 @@ class Void extends React.Component {
|
||||
isVoid
|
||||
renderMark={noop}
|
||||
key={offsetKey}
|
||||
schema={schema}
|
||||
state={state}
|
||||
node={child}
|
||||
ranges={ranges}
|
||||
|
@ -1,92 +0,0 @@
|
||||
|
||||
/**
|
||||
* The default Slate schema rules, which enforce the most basic constraints.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
|
||||
const RULES = [
|
||||
{
|
||||
match: {
|
||||
kind: 'document'
|
||||
},
|
||||
validate: {
|
||||
anyOf: [
|
||||
{ kind: 'block' }
|
||||
]
|
||||
},
|
||||
transform: (transform, match, reason) => {
|
||||
return reason.value.reduce((tr, node) => {
|
||||
return tr.removeNodeByKey(node.key)
|
||||
}, transform)
|
||||
}
|
||||
},
|
||||
{
|
||||
match: {
|
||||
kind: 'block'
|
||||
},
|
||||
validate: {
|
||||
anyOf: [
|
||||
{ kind: 'block' },
|
||||
{ kind: 'inline' },
|
||||
{ kind: 'text' },
|
||||
]
|
||||
},
|
||||
transform: (transform, match, reason) => {
|
||||
return reason.value.reduce((tr, node) => {
|
||||
return tr.removeNodeByKey(node.key)
|
||||
}, transform)
|
||||
}
|
||||
},
|
||||
{
|
||||
match: {
|
||||
kind: 'inline'
|
||||
},
|
||||
validate: {
|
||||
anyOf: [
|
||||
{ kind: 'inline' },
|
||||
{ kind: 'text' },
|
||||
]
|
||||
},
|
||||
transform: (transform, match, reason) => {
|
||||
return reason.value.reduce((tr, node) => {
|
||||
return tr.removeNodeByKey(node.key)
|
||||
}, transform)
|
||||
}
|
||||
},
|
||||
// {
|
||||
// match: { isVoid: true },
|
||||
// validate: {
|
||||
// text: ' '
|
||||
// },
|
||||
// transform: (transform, node) => {
|
||||
// const { state } = transform
|
||||
// const range = state.selection.moveToRangeOf(node)
|
||||
// return transform.delete().insertText(' ')
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// match: (object) => {
|
||||
// return (
|
||||
// object.kind == 'block' &&
|
||||
// object.nodes.size == 1 &&
|
||||
// object.nodes.first().isVoid
|
||||
// )
|
||||
// },
|
||||
// invalid: true,
|
||||
// transform: (transform, node) => {
|
||||
// const child = node.nodes.first()
|
||||
// const text =
|
||||
// return transform
|
||||
// .insertNodeBeforeNodeByKey(child.)
|
||||
// }
|
||||
// }
|
||||
]
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
|
||||
export default RULES
|
@ -1,6 +1,5 @@
|
||||
|
||||
import React from 'react'
|
||||
import RULES from '../constants/rules'
|
||||
import includes from 'lodash/includes'
|
||||
import isReactComponent from '../utils/is-react-component'
|
||||
import typeOf from 'type-of'
|
||||
@ -171,7 +170,7 @@ class Schema extends new Record(DEFAULTS) {
|
||||
.filter(rule => rule.match(object) && rule.decorator)
|
||||
.map((rule) => {
|
||||
return (text) => {
|
||||
return rule.decorator(object, text)
|
||||
return rule.decorator(text, object)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -250,7 +249,7 @@ function normalizeNodes(nodes) {
|
||||
type: key,
|
||||
}
|
||||
|
||||
if (value.component) {
|
||||
if (value.component || value.decorator || value.validate) {
|
||||
rules.push({
|
||||
match,
|
||||
...value,
|
||||
@ -283,7 +282,7 @@ function normalizeMarks(marks) {
|
||||
type: key,
|
||||
}
|
||||
|
||||
if (value.component) {
|
||||
if (value.component || value.decorator || value.validate) {
|
||||
rules.push({
|
||||
match,
|
||||
...value,
|
||||
@ -392,9 +391,9 @@ function normalizeTransform(transform) {
|
||||
|
||||
function normalizeSpec(obj, giveReason) {
|
||||
const spec = { ...obj }
|
||||
if (spec.exactlyOf) spec.exactlyOf = spec.exactlyOf.map(normalizeSpec)
|
||||
if (spec.anyOf) spec.anyOf = spec.anyOf.map(normalizeSpec)
|
||||
if (spec.noneOf) spec.noneOf = spec.noneOf.map(normalizeSpec)
|
||||
if (spec.exactlyOf) spec.exactlyOf = spec.exactlyOf.map(s => normalizeSpec(s))
|
||||
if (spec.anyOf) spec.anyOf = spec.anyOf.map(s => normalizeSpec(s))
|
||||
if (spec.noneOf) spec.noneOf = spec.noneOf.map(s => normalizeSpec(s))
|
||||
|
||||
return (node) => {
|
||||
for (const key in CHECKS) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
"browserify-shim": "^3.8.12",
|
||||
"disc": "^1.3.2",
|
||||
"envify": "^3.4.1",
|
||||
"enzyme": "^2.4.1",
|
||||
"eslint": "^3.0.1",
|
||||
"eslint-plugin-import": "^1.10.2",
|
||||
"eslint-plugin-react": "^5.2.2",
|
||||
@ -46,10 +47,12 @@
|
||||
"is-image": "^1.0.1",
|
||||
"is-url": "^1.2.2",
|
||||
"mocha": "^2.5.3",
|
||||
"mocha-jsdom": "^1.1.0",
|
||||
"npm-run-all": "^2.3.0",
|
||||
"prismjs": "^1.5.1",
|
||||
"react": "^15.2.0",
|
||||
"react-addons-perf": "^15.2.1",
|
||||
"react-addons-test-utils": "^15.3.0",
|
||||
"react-dom": "^15.1.0",
|
||||
"react-frame-aware-selection-plugin": "0.0.2",
|
||||
"react-frame-component": "^0.6.2",
|
||||
|
@ -7,6 +7,8 @@ function Image(props) {
|
||||
)
|
||||
}
|
||||
|
||||
export function renderNode(node) {
|
||||
if (node.type == 'image') return Image
|
||||
export const schema = {
|
||||
nodes: {
|
||||
image: Image
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ function Code(props) {
|
||||
return <pre {...props.attributes}><code>{props.children}</code></pre>
|
||||
}
|
||||
|
||||
export function renderNode(node) {
|
||||
if (node.type == 'code') return Code
|
||||
export const schema = {
|
||||
nodes: {
|
||||
code: Code
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,11 @@
|
||||
|
||||
import React from 'react'
|
||||
import { Mark } from '../../../..'
|
||||
|
||||
const BOLD = {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
|
||||
export function renderMark(mark) {
|
||||
if (mark.type == 'bold') return BOLD
|
||||
}
|
||||
|
||||
export function renderDecorations(text) {
|
||||
function decorator(text) {
|
||||
let { characters } = text
|
||||
let second = characters.get(1)
|
||||
let mark = Mark.create({ type: 'bold' })
|
||||
@ -19,3 +14,14 @@ export function renderDecorations(text) {
|
||||
characters = characters.set(1, second)
|
||||
return characters
|
||||
}
|
||||
|
||||
export const schema = {
|
||||
nodes: {
|
||||
default: {
|
||||
decorator
|
||||
}
|
||||
},
|
||||
marks: {
|
||||
bold: BOLD
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ function Link(props) {
|
||||
return <a {...props.attributes} href={href}>{props.children}</a>
|
||||
}
|
||||
|
||||
export function renderNode(node) {
|
||||
if (node.type == 'link') return Link
|
||||
export const schema = {
|
||||
nodes: {
|
||||
link: Link
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
|
||||
import React from 'react'
|
||||
|
||||
const BOLD = {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
|
||||
const ITALIC = {
|
||||
fontStyle: 'italic'
|
||||
}
|
||||
|
||||
const BOLD_ITALIC = {
|
||||
fontFamily: 'bold-italic'
|
||||
}
|
||||
|
||||
export function renderMark(mark, marks) {
|
||||
if (
|
||||
marks.size > 1 &&
|
||||
marks.some(m => m.type == 'bold') &&
|
||||
marks.some(m => m.type == 'italic')
|
||||
) {
|
||||
return mark.type == 'bold'
|
||||
? BOLD_ITALIC
|
||||
: null
|
||||
}
|
||||
|
||||
if (mark.type == 'bold') return BOLD
|
||||
if (mark.type == 'italic') return ITALIC
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: default
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: one
|
||||
marks:
|
||||
- type: bold
|
||||
- text: two
|
||||
marks:
|
||||
- type: italic
|
||||
- text: three
|
||||
marks:
|
||||
- type: bold
|
||||
- type: italic
|
@ -1,10 +0,0 @@
|
||||
|
||||
<div contenteditable="true">
|
||||
<div style="position:relative;">
|
||||
<span>
|
||||
<span><span style="font-weight:bold;">one</span></span>
|
||||
<span><span style="font-style:italic;">two</span></span>
|
||||
<span><span style="font-family:bold-italic;">three</span></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
@ -7,6 +7,8 @@ class Bold extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export function renderMark(mark) {
|
||||
if (mark.type == 'bold') return Bold
|
||||
export const schema = {
|
||||
marks: {
|
||||
bold: Bold
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
|
||||
import React from 'react'
|
||||
|
||||
function BOLD(props) {
|
||||
function Bold(props) {
|
||||
return <strong>{props.children}</strong>
|
||||
}
|
||||
|
||||
export function renderMark(mark) {
|
||||
if (mark.type == 'bold') return BOLD
|
||||
export const schema = {
|
||||
marks: {
|
||||
bold: Bold
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ function ITALIC(props) {
|
||||
return <i>{props.children}</i>
|
||||
}
|
||||
|
||||
export function renderMark(mark, marks) {
|
||||
if (mark.type == 'bold') return BOLD
|
||||
if (mark.type == 'italic') return ITALIC
|
||||
export const schema = {
|
||||
marks: {
|
||||
bold: BOLD,
|
||||
italic: ITALIC
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export function renderMark(mark) {
|
||||
if (mark.type == 'bold') {
|
||||
return {
|
||||
export const schema = {
|
||||
marks: {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export function renderMark(mark) {
|
||||
if (mark.type == 'bold') return 'bold'
|
||||
export const schema = {
|
||||
marks: {
|
||||
bold: 'bold'
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ function Code(props) {
|
||||
return <pre {...props.attributes}><code>{props.children}</code></pre>
|
||||
}
|
||||
|
||||
export function renderNode(node) {
|
||||
if (node.type == 'code') return Code
|
||||
export const schema = {
|
||||
nodes: {
|
||||
code: Code
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ function Link(props) {
|
||||
return <a {...props.attributes} href={href}>{props.children}</a>
|
||||
}
|
||||
|
||||
export function renderNode(node) {
|
||||
if (node.type == 'link') return Link
|
||||
export const schema = {
|
||||
nodes: {
|
||||
link: Link
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,21 @@
|
||||
|
||||
import React from 'react'
|
||||
import fs from 'fs'
|
||||
import strip from '../helpers/strip-dynamic'
|
||||
import jsdom from 'mocha-jsdom'
|
||||
import readMetadata from 'read-metadata'
|
||||
import strip from '../helpers/strip-dynamic'
|
||||
import { Raw, Editor, Schema } from '../..'
|
||||
import { strictEqual } from '../helpers/assert-json'
|
||||
import { mount } from 'enzyme'
|
||||
import { resolve } from 'path'
|
||||
import { strictEqual } from '../helpers/assert-json'
|
||||
|
||||
/**
|
||||
* Tests.
|
||||
*/
|
||||
|
||||
describe('schema', () => {
|
||||
jsdom()
|
||||
|
||||
const tests = fs.readdirSync(resolve(__dirname, './fixtures'))
|
||||
|
||||
for (const test of tests) {
|
||||
@ -22,10 +26,16 @@ describe('schema', () => {
|
||||
const input = readMetadata.sync(resolve(dir, 'input.yaml'))
|
||||
const expected = readMetadata.sync(resolve(dir, 'output.yaml'))
|
||||
const schema = Schema.create(require(dir))
|
||||
|
||||
const state = Raw.deserialize(input, { terse: true })
|
||||
const editor = <Editor state={state} schema={schema} />
|
||||
const output = Raw.serialize(state, { terse: true })
|
||||
const props = {
|
||||
onChange: value => value,
|
||||
schema,
|
||||
state,
|
||||
}
|
||||
|
||||
const wrapper = mount(<Editor {...props} />)
|
||||
const normalized = wrapper.state().state
|
||||
const output = Raw.serialize(normalized, { terse: true })
|
||||
strictEqual(strip(output), strip(expected))
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user