1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-23 16:55:23 +01:00
slate/lib/components/void.js

188 lines
3.7 KiB
JavaScript

import Leaf from './leaf'
import Mark from '../models/mark'
import OffsetKey from '../utils/offset-key'
import React from 'react'
import ReactDOM from 'react-dom'
import keycode from 'keycode'
import { IS_FIREFOX } from '../utils/environment'
/**
* Void.
*
* @type {Component}
*/
class Void extends React.Component {
/**
* Property types.
*/
static propTypes = {
children: React.PropTypes.any.isRequired,
className: React.PropTypes.string,
editor: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired,
state: React.PropTypes.object.isRequired,
style: React.PropTypes.object
};
/**
* Default properties.
*/
static defaultProps = {
style: {}
}
/**
* Should the component update?
*
* @param {Object} props
* @param {Object} state
* @return {Boolean}
*/
shouldComponentUpdate = (props, state) => {
return (
props.node != this.props.node ||
(props.state.isFocused && props.state.selection.hasEdgeIn(props.node))
)
}
/**
* When one of the wrapper elements it clicked, select the void node.
*
* @param {Event} e
*/
onClick = (e) => {
e.preventDefault()
const { state, node, editor } = this.props
const next = state
.transform()
.moveToRangeOf(node)
.focus()
.apply()
editor.onChange(next)
}
/**
* Render.
*
* @return {Element}
*/
render = () => {
const { children, node, className, style } = this.props
const Tag = node.kind == 'block' ? 'div' : 'span'
// Make the outer wrapper relative, so the spacer can overlay it.
const styles = {
...style,
position: 'relative'
}
return (
<Tag
contentEditable={false}
data-void="true"
onClick={this.onClick}
>
<Tag
contentEditable
suppressContentEditableWarning
className={className}
style={styles}
>
{this.renderSpacer()}
<Tag contentEditable={false}>{children}</Tag>
</Tag>
</Tag>
)
}
/**
* Render a fake spacer leaf, which will catch the cursor when it the void
* node is navigated to with the arrow keys. Having this spacer there means
* the browser continues to manage the selection natively, so it keeps track
* of the right offset when moving across the block.
*
* @return {Element}
*/
renderSpacer = () => {
// COMPAT: In Firefox, if the <span> is positioned absolutely, it won't
// receive the cursor properly when navigating via arrow keys.
const style = IS_FIREFOX
? {
pointerEvents: 'none',
width: '0px',
height: '0px',
lineHeight: '0px',
visibility: 'hidden'
}
: {
position: 'absolute',
top: '0px',
left: '-9999px',
textIndent: '-9999px'
}
return (
<span style={style}>{this.renderLeaf()}</span>
)
}
/**
* Render a fake leaf.
*
* @return {Element}
*/
renderLeaf = () => {
const { node, state } = this.props
const child = node.getTexts().first()
const ranges = child.getRanges()
const text = ''
const marks = Mark.createSet()
const index = 0
const offsetKey = OffsetKey.stringify({
key: child.key,
index
})
return (
<Leaf
renderMark={this.renderLeafMark}
key={offsetKey}
state={state}
node={child}
ranges={ranges}
index={index}
text={text}
marks={marks}
/>
)
}
/**
* Render a fake leaf mark.
*
* @return {Object}
*/
renderLeafMark = (mark) => {
return {}
}
}
/**
* Export.
*/
export default Void