1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-21 13:51:59 +02:00
This commit is contained in:
Ian Storm Taylor 2016-08-14 13:21:46 -07:00
parent dbcb9e531f
commit ccac6102a5
34 changed files with 260 additions and 557 deletions
examples
auto-markdown
code-highlighting
development/performance-rich
embeds
hovering-menu
iframes
images
index.css
links
paste-html
rich-text
rtl
tables
lib
package.json
test
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))
})
}