1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-07-31 12:30:11 +02:00

add wrap transform

This commit is contained in:
Ian Storm Taylor
2016-06-21 18:00:18 -07:00
parent 38b85fe720
commit fccf762efd
9 changed files with 110 additions and 53 deletions

View File

@@ -63,7 +63,7 @@ class App extends React.Component {
return (props) => <blockquote>{props.children}</blockquote> return (props) => <blockquote>{props.children}</blockquote>
} }
case 'bulleted-list': { case 'bulleted-list': {
return (props) => <ul>{props.chidlren}</ul> return (props) => <ul>{props.children}</ul>
} }
case 'heading-one': { case 'heading-one': {
return (props) => <h1>{props.children}</h1> return (props) => <h1>{props.children}</h1>
@@ -84,10 +84,7 @@ class App extends React.Component {
return (props) => <h6>{props.children}</h6> return (props) => <h6>{props.children}</h6>
} }
case 'list-item': { case 'list-item': {
return (props) => <li>{props.chidlren}</li> return (props) => <li>{props.children}</li>
}
case 'numbered-list': {
return (props) => <ol>{props.children}</ol>
} }
case 'paragraph': { case 'paragraph': {
return (props) => <p>{props.children}</p> return (props) => <p>{props.children}</p>
@@ -153,24 +150,28 @@ class App extends React.Component {
case '>': case '>':
transform = transform.setType('block-quote') transform = transform.setType('block-quote')
break break
case '*':
case '-': case '-':
transform = transform.setType('list-item') case '+':
const wrapper = document.getParentNode(node) if (node.type == 'list-item') break
if (wrapper.type == 'paragraph') transform = transform.setType('bulleted-list') transform = node.type == 'list-item'
if (wrapper.type == 'bulleted-list') transform = transform.wrap('list-item') ? transform
if (wrapper.type == 'list-item') transform = transform.wrap('unordered-list') : transform
.setType('list-item')
.wrap('bulleted-list')
break break
default: default:
return return
} }
e.preventDefault()
state = transform state = transform
.deleteAtRange(selection.extendBackwardToStartOf(node)) .deleteAtRange(selection.extendBackwardToStartOf(node))
.apply() .apply()
selection = selection.moveToStartOf(node) selection = selection.moveToStartOf(node)
state = state.merge({ selection }) state = state.merge({ selection })
e.preventDefault()
return state return state
} }
@@ -189,12 +190,16 @@ class App extends React.Component {
const node = state.currentBlockNodes.first() const node = state.currentBlockNodes.first()
if (!node) debugger if (!node) debugger
if (node.type == 'paragraph') return if (node.type == 'paragraph') return
e.preventDefault() e.preventDefault()
return state
let transform = state
.transform() .transform()
.setType('paragraph') .setType('paragraph')
.apply()
if (node.type == 'list-item') transform = transform.unwrap('bulleted-list')
state = transform.apply()
return state
} }
/** /**

View File

@@ -145,7 +145,6 @@ class Content extends React.Component {
*/ */
render() { render() {
console.log('Rendered content.')
const { state } = this.props const { state } = this.props
const { document } = state const { document } = state
const children = document.nodes const children = document.nodes
@@ -175,39 +174,65 @@ class Content extends React.Component {
} }
/** /**
* Render a single `node`. * Render a `node`.
* *
* @param {Node} node * @param {Node} node
* @return {Component} component * @return {Component} component
*/ */
renderNode(node) { renderNode(node) {
const { renderMark, renderNode, state } = this.props switch (node.kind) {
case 'text':
if (node instanceof TextModel) { return this.renderText(node)
return ( case 'block':
<Text case 'inline':
key={node.key} return this.renderElement(node)
node={node}
renderMark={renderMark}
state={state}
/>
)
} }
}
/**
* Render a text `node`.
*
* @param {Node} node
* @return {Component} component
*/
renderText(node) {
const { renderMark, renderNode, state } = this.props
return (
<Text
key={node.key}
node={node}
renderMark={renderMark}
state={state}
/>
)
}
/**
* Render an element `node`.
*
* @param {Node} node
* @return {Component} component
*/
renderElement(node) {
const { renderNode, state } = this.props
const Component = renderNode(node) const Component = renderNode(node)
const children = node.nodes const children = node.nodes
.map(node => this.renderNode(node)) .map(child => this.renderNode(child))
.toArray() .toArray()
return ( return (
<Component <Component
{...node}
key={node.key} key={node.key}
children={children} node={node}
state={state} state={state}
/> >
{children}
</Component>
) )
} }
} }

View File

@@ -1,7 +1,7 @@
import Node from './node' import Node from './node'
import uid from 'uid' import uid from 'uid'
import { OrderedMap, Record } from 'immutable' import { Map, OrderedMap, Record } from 'immutable'
/** /**
* Default properties. * Default properties.

View File

@@ -7,8 +7,7 @@ import { OrderedMap, Record } from 'immutable'
*/ */
const DEFAULTS = { const DEFAULTS = {
nodes: new OrderedMap(), nodes: new OrderedMap()
parent: null
} }
/** /**

View File

@@ -1,7 +1,7 @@
import Node from './node' import Node from './node'
import uid from 'uid' import uid from 'uid'
import { OrderedMap, Record } from 'immutable' import { Map, OrderedMap, Record } from 'immutable'
/** /**
* Record. * Record.

View File

@@ -886,32 +886,42 @@ const Node = {
}, },
/** /**
* Wrap all of the nodes in a `range` in a new `parent` node. * Wrap all of the nodes in a `range` in a new parent node of `type`.
* *
* @param {Selection} range * @param {Selection} range
* @param {Node or String} parent * @param {String} type
* @return {Node} node * @return {Node} node
*/ */
wrapAtRange(range, parent) { wrapAtRange(range, type) {
range = range.normalize(this)
let node = this let node = this
range = range.normalize(node) let blocks = node.getBlockNodesAtRange(range)
// Allow for the parent to by just a type. // Iterate each of the block nodes, wrapping them.
if (typeof parent == 'string') { blocks.forEach((block) => {
parent = Block.create({ type: parent }) let isDirectChild = node.nodes.has(block.key)
} let parent = isDirectChild ? node : node.getParentNode(block)
// Add the child to the parent's nodes. // Create a new wrapper containing the block.
const child = node.findNode(key) let nodes = Block.createMap([ block ])
parent = node.nodes.set(child.key, child) let wrapper = Block.create({ type, nodes })
// Remove the child from this node. // Replace the block in it's parent with the wrapper.
node = node.removeNode(child) nodes = parent.nodes.takeUntil(node => node == block)
.set(wrapper.key, wrapper)
.concat(parent.nodes.skipUntil(node => node == block).rest())
// Add the parent to this node. // Update the parent.
if (isDirectChild) {
node = node.merge({ nodes })
} else {
parent = parent.merge({ nodes })
node = node.updateNode(parent)
}
})
return node return node.normalize()
} }
/** /**

View File

@@ -338,6 +338,21 @@ class State extends Record(DEFAULTS) {
return state return state
} }
/**
* Wrap the block nodes in the current selection in new nodes of `type`.
*
* @param {String} type
* @return {State} state
*/
wrap(type) {
let state = this
let { document, selection } = state
document = document.wrapAtRange(selection, type)
state = state.merge({ document })
return state
}
} }
/** /**

View File

@@ -8,8 +8,7 @@ import { List, Record } from 'immutable'
const DEFAULTS = { const DEFAULTS = {
characters: new List(), characters: new List(),
key: null, key: null
parent: null
} }
/** /**

View File

@@ -51,7 +51,11 @@ const TRANSFORM_TYPES = [
'split', 'split',
'splitAtRange', 'splitAtRange',
'unmark', 'unmark',
'unmarkAtRange' 'unmarkAtRange',
'unwrap',
'unwrapAtRange',
'wrap',
'wrapAtRange'
] ]
/** /**