From b1a56f60cda6817e699ad4b2ab4dd780d541a6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Zen?= Date: Tue, 2 Aug 2016 16:17:37 -0300 Subject: [PATCH] Custom block components + soft break handling --- src/components/Widgets/MarkdownControl.js | 49 +++++++++---- .../Widgets/MarkdownControlElements/Block.css | 71 +++++++++++++++++++ .../Widgets/MarkdownControlElements/Block.js | 32 +++++++++ 3 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 src/components/Widgets/MarkdownControlElements/Block.css create mode 100644 src/components/Widgets/MarkdownControlElements/Block.js diff --git a/src/components/Widgets/MarkdownControl.js b/src/components/Widgets/MarkdownControl.js index 59c911d4..aac1d894 100644 --- a/src/components/Widgets/MarkdownControl.js +++ b/src/components/Widgets/MarkdownControl.js @@ -1,9 +1,10 @@ import React, { PropTypes } from 'react'; -import { Editor } from 'slate'; +import { Editor, Plain } from 'slate'; import Markdown from 'slate-markdown-serializer'; +import Block from './MarkdownControlElements/Block'; +import { Icon } from '../UI'; + const markdown = new Markdown(); - - /* * Slate Render Configuration */ @@ -13,16 +14,20 @@ const DEFAULT_NODE = 'paragraph'; // Local node renderers. const NODES = { - 'block-quote': (props) =>
{props.children}
, - 'bulleted-list': props => , - 'heading1': props =>

{props.children}

, - 'heading2': props =>

{props.children}

, + 'block-quote': (props) => {props.children}, + 'bulleted-list': props => , + 'heading1': props => {props.children}, + 'heading2': props => {props.children}, + 'heading3': props => {props.children}, + 'heading4': props => {props.children}, + 'heading5': props => {props.children}, + 'heading6': props => {props.children}, 'list-item': props =>
  • {props.children}
  • , - 'paragraph': props =>

    {props.children}

    , + 'paragraph': props => {props.children}, 'link': (props) => { const { data } = props.node; const href = data.get('href'); - return {props.children}; + return {props.children}; }, 'image': (props) => { const { node, state } = props; @@ -52,8 +57,9 @@ const MARKS = { class MarkdownControl extends React.Component { constructor(props) { super(props); + this.blockEdit = false; this.state = { - state: markdown.deserialize(props.value || '') + state: props.value ? markdown.deserialize(props.value) : Plain.deserialize('') }; this.hasMark = this.hasMark.bind(this); @@ -62,6 +68,7 @@ class MarkdownControl extends React.Component { this.handleDocumentChange = this.handleDocumentChange.bind(this); this.onClickMark = this.onClickMark.bind(this); this.onClickBlock = this.onClickBlock.bind(this); + this.handleKeyDown = this.handleKeyDown.bind(this); this.renderToolbar = this.renderToolbar.bind(this); this.renderMarkButton = this.renderMarkButton.bind(this); this.renderBlockButton = this.renderBlockButton.bind(this); @@ -88,7 +95,11 @@ class MarkdownControl extends React.Component { * content changes */ handleChange(state) { - this.setState({ state }); + if (this.blockEdit) { + this.blockEdit = false; + } else { + this.setState({ state }); + } } handleDocumentChange(document, state) { @@ -100,7 +111,6 @@ class MarkdownControl extends React.Component { * Toggle marks / blocks when button is clicked */ onClickMark(e, type) { - e.preventDefault(); let { state } = this.state; state = state @@ -111,6 +121,19 @@ class MarkdownControl extends React.Component { this.setState({ state }); } + handleKeyDown(evt) { + if (evt.shiftKey && evt.key === 'Enter') { + this.blockEdit = true; + let { state } = this.state; + state = state + .transform() + .insertText(' \n') + .apply(); + + this.setState({ state }); + } + } + onClickBlock(e, type) { e.preventDefault(); let { state } = this.state; @@ -167,6 +190,7 @@ class MarkdownControl extends React.Component { {this.renderMarkButton('bold', 'b')} {this.renderMarkButton('italic', 'i')} {this.renderMarkButton('code', 'code')} + {this.renderMarkButton('linebreak', 'break')} {this.renderBlockButton('heading1', 'h1')} {this.renderBlockButton('heading2', 'h2')} {this.renderBlockButton('block-quote', 'blockquote')} @@ -219,6 +243,7 @@ class MarkdownControl extends React.Component { renderNode={this.renderNode} renderMark={this.renderMark} onChange={this.handleChange} + onKeyDown={this.handleKeyDown} onDocumentChange={this.handleDocumentChange} /> diff --git a/src/components/Widgets/MarkdownControlElements/Block.css b/src/components/Widgets/MarkdownControlElements/Block.css new file mode 100644 index 00000000..df0af926 --- /dev/null +++ b/src/components/Widgets/MarkdownControlElements/Block.css @@ -0,0 +1,71 @@ +.root { + border: dotted 1px #ddd; + position: relative; + margin: 9px 0 15px 0; +} + + +.type:after { + content: attr(data-type); + font-size: 10px; + color: #aaa; + position: absolute; + top : -7px;; + margin-left: 1em; + padding: 0 3px; + display: inline; + background-color: #fafafa; + pointer-events: none; +} + +.body { + padding: 8px; +} + +.body img{ + max-width: 100%; + height: auto; +} + +.Paragraph { + +} + +.Heading1, .Heading2, .Heading3, .Heading4, .Heading5, .Heading6 { + margin: 0; + font-weight: bold +} + +.Heading1 { + font-size: 1.2em; +} + +.Heading2 { + font-size: 1.15em; +} + +.Heading3 { + font-size: 1.1em; +} + +.Heading4 { + font-size: 1.07em; +} + +.Heading5 { + font-size: 1.05em; +} + +.Heading6 { + font-size: 1.03em; +} + +.blockquote { + padding-left: 5px; + border-left: solid 3px #ccc; +} + +.body ul { + padding-left: 20px; + margin: 0; +} diff --git a/src/components/Widgets/MarkdownControlElements/Block.js b/src/components/Widgets/MarkdownControlElements/Block.js new file mode 100644 index 00000000..462165c4 --- /dev/null +++ b/src/components/Widgets/MarkdownControlElements/Block.js @@ -0,0 +1,32 @@ +import React, { PropTypes } from 'react'; +import styles from './Block.css'; + +const AVAILABLE_TYPES = [ + 'Paragraph', + 'Heading1', + 'Heading2', + 'Heading3', + 'Heading4', + 'Heading5', + 'Heading6', + 'ul', + 'blockquote' +]; + +export function Block({ type, children }) { + return ( +
    +
    +
    + {children} +
    +
    + ); +} + +Block.propTypes = { + children: PropTypes.node.isRequired, + type: PropTypes.oneOf(AVAILABLE_TYPES).isRequired +}; + +export default Block;