updated slate version
This commit is contained in:
parent
474abd8f78
commit
dea734ec21
@ -5,17 +5,17 @@ import { connect } from 'react-redux';
|
|||||||
import { switchVisualMode } from '../../actions/editor';
|
import { switchVisualMode } from '../../actions/editor';
|
||||||
|
|
||||||
class MarkdownControl extends React.Component {
|
class MarkdownControl extends React.Component {
|
||||||
constructor(props){
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.useVisualEditor = this.useVisualEditor.bind(this);
|
this.useVisualEditor = this.useVisualEditor.bind(this);
|
||||||
this.useRawEditor = this.useRawEditor.bind(this);
|
this.useRawEditor = this.useRawEditor.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
useVisualEditor(){
|
useVisualEditor() {
|
||||||
this.props.switchVisualMode(true);
|
this.props.switchVisualMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
useRawEditor(){
|
useRawEditor() {
|
||||||
this.props.switchVisualMode(false);
|
this.props.switchVisualMode(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +29,7 @@ class MarkdownControl extends React.Component {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onAddMedia={onAddMedia}
|
onAddMedia={onAddMedia}
|
||||||
getMedia={getMedia}
|
getMedia={getMedia}
|
||||||
|
registeredComponents={editor.get('registeredComponents')}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,25 +4,6 @@ import Prism from 'prismjs';
|
|||||||
import marks from './prismMarkdown';
|
import marks from './prismMarkdown';
|
||||||
import styles from './index.css';
|
import styles from './index.css';
|
||||||
|
|
||||||
const MARKS = {
|
|
||||||
'highlight-comment': {
|
|
||||||
opacity: '0.33'
|
|
||||||
},
|
|
||||||
'highlight-important': {
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#006',
|
|
||||||
},
|
|
||||||
'highlight-keyword': {
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#006',
|
|
||||||
},
|
|
||||||
'highlight-url': {
|
|
||||||
color: '#006',
|
|
||||||
},
|
|
||||||
'highlight-punctuation': {
|
|
||||||
color: '#006',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Prism.languages.markdown = Prism.languages.extend('markup', {});
|
Prism.languages.markdown = Prism.languages.extend('markup', {});
|
||||||
Prism.languages.insertBefore('markdown', 'prolog', marks);
|
Prism.languages.insertBefore('markdown', 'prolog', marks);
|
||||||
@ -31,44 +12,7 @@ Prism.languages.markdown['italic'].inside['url'] = Prism.util.clone(Prism.langua
|
|||||||
Prism.languages.markdown['bold'].inside['italic'] = Prism.util.clone(Prism.languages.markdown['italic']);
|
Prism.languages.markdown['bold'].inside['italic'] = Prism.util.clone(Prism.languages.markdown['italic']);
|
||||||
Prism.languages.markdown['italic'].inside['bold'] = Prism.util.clone(Prism.languages.markdown['bold']);
|
Prism.languages.markdown['italic'].inside['bold'] = Prism.util.clone(Prism.languages.markdown['bold']);
|
||||||
|
|
||||||
class RawEditor extends React.Component {
|
function renderDecorations(text, block) {
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
const content = props.value ? Plain.deserialize(props.value) : Plain.deserialize('');
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
state: content
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handleChange = this.handleChange.bind(this);
|
|
||||||
this.handleDocumentChange = this.handleDocumentChange.bind(this);
|
|
||||||
this.renderMark = this.renderMark.bind(this);
|
|
||||||
this.renderDecorations = this.renderDecorations.bind(this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Slate keeps track of selections, scroll position etc.
|
|
||||||
* So, onChange gets dispatched on every interaction (click, arrows, everything...)
|
|
||||||
* It also have an onDocumentChange, that get's dispached only when the actual
|
|
||||||
* content changes
|
|
||||||
*/
|
|
||||||
handleChange(state) {
|
|
||||||
this.setState({ state });
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDocumentChange(document, state) {
|
|
||||||
const content = Plain.serialize(state, { terse: true });
|
|
||||||
this.props.onChange(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMark(mark) {
|
|
||||||
return MARKS[mark.type] || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
renderDecorations(text, block) {
|
|
||||||
let characters = text.characters.asMutable();
|
let characters = text.characters.asMutable();
|
||||||
const string = text.text;
|
const string = text.text;
|
||||||
const grammar = Prism.languages.markdown;
|
const grammar = Prism.languages.markdown;
|
||||||
@ -97,15 +41,74 @@ class RawEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return characters.asImmutable();
|
return characters.asImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const SCHEMA = {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
match: (object) => object.kind == 'block',
|
||||||
|
decorate: renderDecorations
|
||||||
|
}
|
||||||
|
],
|
||||||
|
marks: {
|
||||||
|
'highlight-comment': {
|
||||||
|
opacity: '0.33'
|
||||||
|
},
|
||||||
|
'highlight-important': {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#006',
|
||||||
|
},
|
||||||
|
'highlight-keyword': {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#006',
|
||||||
|
},
|
||||||
|
'highlight-url': {
|
||||||
|
color: '#006',
|
||||||
|
},
|
||||||
|
'highlight-punctuation': {
|
||||||
|
color: '#006',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RawEditor extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const content = props.value ? Plain.deserialize(props.value) : Plain.deserialize('');
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
state: content
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
this.handleDocumentChange = this.handleDocumentChange.bind(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slate keeps track of selections, scroll position etc.
|
||||||
|
* So, onChange gets dispatched on every interaction (click, arrows, everything...)
|
||||||
|
* It also have an onDocumentChange, that get's dispached only when the actual
|
||||||
|
* content changes
|
||||||
|
*/
|
||||||
|
handleChange(state) {
|
||||||
|
this.setState({ state });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDocumentChange(document, state) {
|
||||||
|
const content = Plain.serialize(state, { terse: true });
|
||||||
|
this.props.onChange(content);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
placeholder={'Enter some rich text...'}
|
placeholder={'Enter some rich text...'}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
renderMark={this.renderMark}
|
schema={SCHEMA}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
onDocumentChange={this.handleDocumentChange}
|
onDocumentChange={this.handleDocumentChange}
|
||||||
renderDecorations={this.renderDecorations}
|
renderDecorations={this.renderDecorations}
|
||||||
|
@ -5,7 +5,7 @@ import position from 'selection-position';
|
|||||||
import MarkupIt, { SlateUtils } from 'markup-it';
|
import MarkupIt, { SlateUtils } from 'markup-it';
|
||||||
import getSyntax from '../syntax';
|
import getSyntax from '../syntax';
|
||||||
import { emptyParagraphBlock } from '../constants';
|
import { emptyParagraphBlock } from '../constants';
|
||||||
import { DEFAULT_NODE, NODES, MARKS } from './localRenderers';
|
import { DEFAULT_NODE, SCHEMA } from './schema';
|
||||||
import StylesMenu from './StylesMenu';
|
import StylesMenu from './StylesMenu';
|
||||||
import BlockTypesMenu from './BlockTypesMenu';
|
import BlockTypesMenu from './BlockTypesMenu';
|
||||||
import styles from './index.css';
|
import styles from './index.css';
|
||||||
@ -22,7 +22,7 @@ class VisualEditor extends React.Component {
|
|||||||
this.markdown = new MarkupIt(MarkdownSyntax);
|
this.markdown = new MarkupIt(MarkdownSyntax);
|
||||||
|
|
||||||
this.customImageNodeRenderer = this.customImageNodeRenderer.bind(this);
|
this.customImageNodeRenderer = this.customImageNodeRenderer.bind(this);
|
||||||
NODES['mediaproxy'] = this.customImageNodeRenderer;
|
SCHEMA.nodes['mediaproxy'] = this.customImageNodeRenderer;
|
||||||
|
|
||||||
this.blockEdit = false;
|
this.blockEdit = false;
|
||||||
this.menuPositions = {
|
this.menuPositions = {
|
||||||
@ -63,8 +63,6 @@ class VisualEditor extends React.Component {
|
|||||||
this.calculateHoverMenuPosition = _.throttle(this.calculateHoverMenuPosition.bind(this), 30);
|
this.calculateHoverMenuPosition = _.throttle(this.calculateHoverMenuPosition.bind(this), 30);
|
||||||
this.calculateBlockMenuPosition = _.throttle(this.calculateBlockMenuPosition.bind(this), 100);
|
this.calculateBlockMenuPosition = _.throttle(this.calculateBlockMenuPosition.bind(this), 100);
|
||||||
this.renderBlockTypesMenu = this.renderBlockTypesMenu.bind(this);
|
this.renderBlockTypesMenu = this.renderBlockTypesMenu.bind(this);
|
||||||
this.renderNode = this.renderNode.bind(this);
|
|
||||||
this.renderMark = this.renderMark.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMedia(src) {
|
getMedia(src) {
|
||||||
@ -290,16 +288,6 @@ class VisualEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return renderers for Slate
|
|
||||||
*/
|
|
||||||
renderNode(node) {
|
|
||||||
return NODES[node.type];
|
|
||||||
}
|
|
||||||
renderMark(mark) {
|
|
||||||
return MARKS[mark.type];
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBlockTypesMenu() {
|
renderBlockTypesMenu() {
|
||||||
const currentBlock = this.state.state.blocks.get(0);
|
const currentBlock = this.state.state.blocks.get(0);
|
||||||
const isOpen = (this.props.value !== undefined && currentBlock.isEmpty && currentBlock.type !== 'horizontal-rule');
|
const isOpen = (this.props.value !== undefined && currentBlock.isEmpty && currentBlock.type !== 'horizontal-rule');
|
||||||
@ -340,8 +328,7 @@ class VisualEditor extends React.Component {
|
|||||||
<Editor
|
<Editor
|
||||||
placeholder={'Enter some rich text...'}
|
placeholder={'Enter some rich text...'}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
renderNode={this.renderNode}
|
schema={SCHEMA}
|
||||||
renderMark={this.renderMark}
|
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
onDocumentChange={this.handleDocumentChange}
|
onDocumentChange={this.handleDocumentChange}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Block from './Block';
|
|
||||||
import styles from './index.css';
|
|
||||||
|
|
||||||
/* eslint react/prop-types: 0, react/no-multi-comp: 0 */
|
|
||||||
|
|
||||||
// Define the default node type.
|
|
||||||
export const DEFAULT_NODE = 'paragraph';
|
|
||||||
|
|
||||||
// Local node renderers.
|
|
||||||
export const NODES = {
|
|
||||||
'blockquote': (props) => <Block type='blockquote' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'unordered_list': props => <Block type='List'><ul {...props.attributes}>{props.children}</ul></Block>,
|
|
||||||
'header_one': props => <Block type='Heading1' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'header_two': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'header_three': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'header_four': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'header_five': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'header_six': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'list_item': props => <li {...props.attributes}>{props.children}</li>,
|
|
||||||
'paragraph': props => <Block type='Paragraph' {...props.attributes}>{props.children}</Block>,
|
|
||||||
'hr': props => {
|
|
||||||
const { node, state } = props;
|
|
||||||
const isFocused = state.selection.hasEdgeIn(node);
|
|
||||||
const className = isFocused ? styles.active : null;
|
|
||||||
return (
|
|
||||||
<hr className={className} {...props.attributes} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
'link': (props) => {
|
|
||||||
const { data } = props.node;
|
|
||||||
const href = data.get('href');
|
|
||||||
return <a {...props.attributes} href={href}>{props.children}</a>;
|
|
||||||
},
|
|
||||||
'image': (props) => {
|
|
||||||
const { node, state } = props;
|
|
||||||
const isFocused = state.selection.hasEdgeIn(node);
|
|
||||||
const className = isFocused ? styles.active : null;
|
|
||||||
const src = node.data.get('src');
|
|
||||||
return (
|
|
||||||
<img {...props.attributes} src={src} className={className} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Local mark renderers.
|
|
||||||
export const MARKS = {
|
|
||||||
BOLD: {
|
|
||||||
fontWeight: 'bold'
|
|
||||||
},
|
|
||||||
ITALIC: {
|
|
||||||
fontStyle: 'italic'
|
|
||||||
},
|
|
||||||
CODE: {
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
backgroundColor: '#eee',
|
|
||||||
padding: '3px',
|
|
||||||
borderRadius: '4px'
|
|
||||||
}
|
|
||||||
};
|
|
@ -0,0 +1,65 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Block from './Block';
|
||||||
|
import styles from './index.css';
|
||||||
|
|
||||||
|
/* eslint react/prop-types: 0, react/no-multi-comp: 0 */
|
||||||
|
|
||||||
|
// Define the default node type.
|
||||||
|
export const DEFAULT_NODE = 'paragraph';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a schema.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const SCHEMA = {
|
||||||
|
nodes: {
|
||||||
|
'blockquote': (props) => <Block type='blockquote' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'unordered_list': props => <Block type='List'><ul {...props.attributes}>{props.children}</ul></Block>,
|
||||||
|
'header_one': props => <Block type='Heading1' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'header_two': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'header_three': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'header_four': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'header_five': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'header_six': props => <Block type='Heading2' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'list_item': props => <li {...props.attributes}>{props.children}</li>,
|
||||||
|
'paragraph': props => <Block type='Paragraph' {...props.attributes}>{props.children}</Block>,
|
||||||
|
'hr': props => {
|
||||||
|
const { node, state } = props;
|
||||||
|
const isFocused = state.selection.hasEdgeIn(node);
|
||||||
|
const className = isFocused ? styles.active : null;
|
||||||
|
return (
|
||||||
|
<hr className={className} {...props.attributes} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'link': (props) => {
|
||||||
|
const { data } = props.node;
|
||||||
|
const href = data.get('href');
|
||||||
|
return <a {...props.attributes} href={href}>{props.children}</a>;
|
||||||
|
},
|
||||||
|
'image': (props) => {
|
||||||
|
const { node, state } = props;
|
||||||
|
const isFocused = state.selection.hasEdgeIn(node);
|
||||||
|
const className = isFocused ? styles.active : null;
|
||||||
|
const src = node.data.get('src');
|
||||||
|
return (
|
||||||
|
<img {...props.attributes} src={src} className={className} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
marks: {
|
||||||
|
BOLD: {
|
||||||
|
fontWeight: 'bold'
|
||||||
|
},
|
||||||
|
ITALIC: {
|
||||||
|
fontStyle: 'italic'
|
||||||
|
},
|
||||||
|
CODE: {
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
backgroundColor: '#eee',
|
||||||
|
padding: '3px',
|
||||||
|
borderRadius: '4px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user