1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-20 15:14:38 +01:00
2016-07-27 16:22:11 -07:00

226 lines
4.9 KiB
JavaScript

import { Editor, Raw } from '../..'
import React from 'react'
import initialState from './state.json'
/**
* Define a set of node renderers.
*
* @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>
}
/**
* The auto-markdown example.
*
* @type {Component}
*/
class AutoMarkdown extends React.Component {
/**
* Deserialize the raw initial state.
*
* @type {Object}
*/
state = {
state: Raw.deserialize(initialState, { terse: true })
};
/**
* Get the block type for a series of auto-markdown shortcut `chars`.
*
* @param {String} chars
* @return {String} block
*/
getType = (chars) => {
switch (chars) {
case '*':
case '-':
case '+': return 'list-item'
case '>': return 'block-quote'
case '#': return 'heading-one'
case '##': return 'heading-two'
case '###': return 'heading-three'
case '####': return 'heading-four'
case '#####': return 'heading-five'
case '######': return 'heading-six'
default: return null
}
}
/**
*
* Render the example.
*
* @return {Component} component
*/
render = () => {
return (
<div className="editor">
<Editor
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.
*
* @param {State} state
*/
onChange = (state) => {
this.setState({ state })
}
/**
* On key down, check for our specific key shortcuts.
*
* @param {Event} e
* @param {Data} data
* @param {State} state
* @return {State or Null} state
*/
onKeyDown = (e, data, state) => {
switch (data.key) {
case 'space': return this.onSpace(e, state)
case 'backspace': return this.onBackspace(e, state)
case 'enter': return this.onEnter(e, state)
}
}
/**
* On space, if it was after an auto-markdown shortcut, convert the current
* node into the shortcut's corresponding type.
*
* @param {Event} e
* @param {State} state
* @return {State or Null} state
*/
onSpace = (e, state) => {
if (state.isExpanded) return
let { selection } = state
const { startText, startBlock, startOffset } = state
const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '')
const type = this.getType(chars)
if (!type) return
if (type == 'list-item' && startBlock.type == 'list-item') return
e.preventDefault()
let transform = state
.transform()
.setBlock(type)
if (type == 'list-item') transform = transform.wrapBlock('bulleted-list')
state = transform
.extendToStartOf(startBlock)
.delete()
.apply()
return state
}
/**
* On backspace, if at the start of a non-paragraph, convert it back into a
* paragraph node.
*
* @param {Event} e
* @param {State} state
* @return {State or Null} state
*/
onBackspace = (e, state) => {
if (state.isExpanded) return
if (state.startOffset != 0) return
const { startBlock } = state
if (startBlock.type == 'paragraph') return
e.preventDefault()
let transform = state
.transform()
.setBlock('paragraph')
if (startBlock.type == 'list-item') transform = transform.unwrapBlock('bulleted-list')
state = transform.apply()
return state
}
/**
* On return, if at the end of a node type that should not be extended,
* create a new paragraph below it.
*
* @param {Event} e
* @param {State} state
* @return {State or Null} state
*/
onEnter = (e, state) => {
if (state.isExpanded) return
const { startBlock, startOffset, endOffset } = state
if (startOffset == 0 && startBlock.length == 0) return this.onBackspace(e, state)
if (endOffset != startBlock.length) return
if (
startBlock.type != 'heading-one' &&
startBlock.type != 'heading-two' &&
startBlock.type != 'heading-three' &&
startBlock.type != 'heading-four' &&
startBlock.type != 'heading-five' &&
startBlock.type != 'heading-six' &&
startBlock.type != 'block-quote'
) {
return
}
e.preventDefault()
return state
.transform()
.splitBlock()
.setBlock('paragraph')
.apply()
}
}
/**
* Export.
*/
export default AutoMarkdown