1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-20 06:01:24 +02:00

Add ability for renderMark to return a React component (#202)

* Switch rendering of leaf to use mark as component

* Backward compatibility with renderMark

Don’t fail on null marks

* Adapt tests for new rendering of marks

* Fix selection with new rendered marks

* Adapt rendering test for custom-mark-multiple

Don’t add span when renderMark return undefined
This commit is contained in:
Samy Pessé
2016-07-29 19:46:56 +02:00
committed by Ian Storm Taylor
parent 788627d2db
commit 4d58c0035c
10 changed files with 99 additions and 27 deletions

View File

@@ -242,17 +242,22 @@ class Editor extends React.Component {
* *
* @param {Mark} mark * @param {Mark} mark
* @param {Set} marks * @param {Set} marks
* @return {Object} * @return {Element}
*/ */
renderMark = (mark, marks) => { renderMark = (mark, marks) => {
for (const plugin of this.state.plugins) { for (const plugin of this.state.plugins) {
if (!plugin.renderMark) continue if (!plugin.renderMark) continue
const style = plugin.renderMark(mark, marks, this.state.state, this) let component = plugin.renderMark(mark, marks, this.state.state, this)
if (style) return style
}
return {} // plugin.renderMak can return a map of style
if (component && !(typeof component === 'function')) {
const style = component
component = (props => <span style={style}>{props.children}</span>)
}
if (component) return component
}
} }
/** /**

View File

@@ -3,6 +3,22 @@ import OffsetKey from '../utils/offset-key'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
/**
* Find the deepest child (text node) of a DOM node
* @param {DOMNode} node
* @return {DOMNode}
*/
function findDeepestNode(node) {
let child = node.firstChild
if (child) {
return findDeepestNode(child)
}
return node
}
/** /**
* Leaf. * Leaf.
*/ */
@@ -86,7 +102,7 @@ class Leaf extends React.Component {
// We have a selection to render, so prepare a few things... // We have a selection to render, so prepare a few things...
const native = window.getSelection() const native = window.getSelection()
const el = ReactDOM.findDOMNode(this).firstChild const el = findDeepestNode(ReactDOM.findDOMNode(this))
// If both the start and end are here, set the selection all at once. // If both the start and end are here, set the selection all at once.
if (hasAnchor && hasFocus) { if (hasAnchor && hasFocus) {
@@ -151,9 +167,8 @@ class Leaf extends React.Component {
<span <span
key={this.tmp.renders} key={this.tmp.renders}
data-offset-key={offsetKey} data-offset-key={offsetKey}
style={this.renderStyle()}
> >
{this.renderText()} {this.renderWithMarks()}
</span> </span>
) )
} }
@@ -172,21 +187,33 @@ class Leaf extends React.Component {
return text return text
} }
renderStyle() { renderMark(inner, mark) {
const { marks, renderMark } = this.props const { node, index, renderMark, marks } = this.props
const style = marks.reduce((memo, mark) => { const offsetKey = OffsetKey.stringify({
const styles = renderMark(mark, marks) key: node.key,
index
})
for (const key in styles) { const MarkComponent = renderMark(mark, marks)
memo[key] = styles[key] if (!MarkComponent) {
return inner
} }
return memo return (
}, {}) <MarkComponent
mark={mark}
return style >
{inner}
</MarkComponent>
)
} }
renderWithMarks() {
const { marks, renderMark } = this.props
const element = marks.reduce(this.renderMark, this.renderText(), this)
return element
}
} }
/** /**

View File

@@ -175,7 +175,7 @@ class Void extends React.Component {
*/ */
renderLeafMark = (mark) => { renderLeafMark = (mark) => {
return {} return
} }
} }

View File

@@ -3,7 +3,7 @@
<div style="position:relative;"> <div style="position:relative;">
<span> <span>
<span>o</span> <span>o</span>
<span style="font-weight:bold;">n</span> <span><span style="font-weight:bold;">n</span></span>
<span>e</span> <span>e</span>
</span> </span>
</div> </div>

View File

@@ -0,0 +1,13 @@
import React from 'react'
const BOLD = {
fontWeight: 'bold'
}
const ITALIC = (props => <i>{props.children}</i>)
export function renderMark(mark, marks) {
if (mark.type == 'bold') return BOLD
if (mark.type == 'italic') return ITALIC
}

View File

@@ -0,0 +1,17 @@
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

View File

@@ -0,0 +1,10 @@
<div contenteditable="true">
<div style="position:relative;">
<span>
<span><span style="font-weight:bold;">one</span></span>
<span><i>two</i></span>
<span><i><span style="font-weight:bold;">three</span></i></span>
</span>
</div>
</div>

View File

@@ -16,10 +16,10 @@ const BOLD_ITALIC = {
export function renderMark(mark, marks) { export function renderMark(mark, marks) {
if ( if (
marks.size > 1 && marks.size > 1 &&
marks.some(m => m.type == 'bold') && marks.some(m => m.type == 'italic') &&
marks.some(m => m.type == 'italic') marks.some(m => m.type == 'bold')
) { ) {
return BOLD_ITALIC return mark.type == 'bold' ? BOLD_ITALIC : undefined
} }
if (mark.type == 'bold') return BOLD if (mark.type == 'bold') return BOLD

View File

@@ -2,9 +2,9 @@
<div contenteditable="true"> <div contenteditable="true">
<div style="position:relative;"> <div style="position:relative;">
<span> <span>
<span style="font-weight:bold;">one</span> <span><span style="font-weight:bold;">one</span></span>
<span style="font-style:italic;">two</span> <span><span style="font-style:italic;">two</span></span>
<span style="font-family:bold-italic;">three</span> <span><span style="font-family:bold-italic;">three</span></span>
</span> </span>
</div> </div>
</div> </div>

View File

@@ -3,7 +3,7 @@
<div style="position:relative;"> <div style="position:relative;">
<span> <span>
<span>one</span> <span>one</span>
<span style="font-weight:bold;">two</span> <span><span style="font-weight:bold;">two</span></span>
<span>three</span> <span>three</span>
</span> </span>
</div> </div>