Implement image uploading for the raw editor

This commit is contained in:
Andrey Okonetchnikov 2016-09-28 14:05:51 +02:00
parent 0a3676204e
commit 47512001ec
3 changed files with 69 additions and 69 deletions

View File

@ -2,6 +2,7 @@ import React, { PropTypes } from 'react';
import { Editor, Plain, Mark } from 'slate'; import { Editor, Plain, Mark } from 'slate';
import Prism from 'prismjs'; import Prism from 'prismjs';
import PluginDropImages from 'slate-drop-or-paste-images'; import PluginDropImages from 'slate-drop-or-paste-images';
import MediaProxy from '../../../../valueObjects/MediaProxy';
import marks from './prismMarkdown'; import marks from './prismMarkdown';
import styles from './index.css'; import styles from './index.css';
@ -71,16 +72,6 @@ const SCHEMA = {
} }
}; };
const plugins = [
PluginDropImages({
applyTransform: (transform, file) => {
const state = Plain.deserialize(`\n\n![${file.name}](${file.name})\n\n`);
return transform
.insertFragment(state.get('document'));
}
})
];
class RawEditor extends React.Component { class RawEditor extends React.Component {
constructor(props) { constructor(props) {
@ -92,9 +83,18 @@ class RawEditor extends React.Component {
state: content state: content
}; };
this.handleChange = this.handleChange.bind(this); this.plugins = [
this.handleDocumentChange = this.handleDocumentChange.bind(this); PluginDropImages({
applyTransform: (transform, file) => {
const mediaProxy = new MediaProxy(file.name, file);
console.log(mediaProxy);
const state = Plain.deserialize(`\n\n![${file.name}](${mediaProxy.public_path})\n\n`);
props.onAddMedia(mediaProxy);
return transform
.insertFragment(state.get('document'));
}
})
];
} }
/** /**
@ -103,11 +103,11 @@ class RawEditor extends React.Component {
* It also have an onDocumentChange, that get's dispatched only when the actual * It also have an onDocumentChange, that get's dispatched only when the actual
* content changes * content changes
*/ */
handleChange(state) { handleChange = state => {
this.setState({ state }); this.setState({ state });
} }
handleDocumentChange(document, state) { handleDocumentChange = (document, state) => {
const content = Plain.serialize(state, { terse: true }); const content = Plain.serialize(state, { terse: true });
this.props.onChange(content); this.props.onChange(content);
} }
@ -121,7 +121,7 @@ class RawEditor extends React.Component {
schema={SCHEMA} schema={SCHEMA}
onChange={this.handleChange} onChange={this.handleChange}
onDocumentChange={this.handleDocumentChange} onDocumentChange={this.handleDocumentChange}
plugins={plugins} plugins={this.plugins}
/> />
); );
} }
@ -130,6 +130,8 @@ class RawEditor extends React.Component {
export default RawEditor; export default RawEditor;
RawEditor.propTypes = { RawEditor.propTypes = {
onAddMedia: PropTypes.func.isRequired,
getMedia: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
value: PropTypes.node, value: PropTypes.string,
}; };

View File

@ -8,7 +8,6 @@ import { DEFAULT_NODE, SCHEMA } from './schema';
import { getNodes, getSyntaxes, getPlugins } from '../../richText'; import { getNodes, getSyntaxes, getPlugins } from '../../richText';
import StylesMenu from './StylesMenu'; import StylesMenu from './StylesMenu';
import BlockTypesMenu from './BlockTypesMenu'; import BlockTypesMenu from './BlockTypesMenu';
import styles from './index.css';
/** /**
* Slate Render Configuration * Slate Render Configuration
@ -178,11 +177,11 @@ class VisualEditor extends React.Component {
} }
/** /**
* When clicking a link, if the selection has a link in it, remove the link. * When clicking a link, if the selection has a link in it, remove the link.
* Otherwise, add a new link with an href and text. * Otherwise, add a new link with an href and text.
* *
* @param {Event} e * @param {Event} e
*/ */
handleInlineClick(type, isActive) { handleInlineClick(type, isActive) {
let { state } = this.state; let { state } = this.state;
@ -212,17 +211,16 @@ class VisualEditor extends React.Component {
this.setState({ state }); this.setState({ state });
} }
handleBlockTypeClick(type) { handleBlockTypeClick(type) {
let { state } = this.state; let { state } = this.state;
state = state state = state
.transform() .transform()
.insertBlock({ .insertBlock({
type: type, type: type,
isVoid: true isVoid: true
}) })
.apply(); .apply();
this.setState({ state }, this.focusAndAddParagraph); this.setState({ state }, this.focusAndAddParagraph);
} }
@ -277,18 +275,17 @@ class VisualEditor extends React.Component {
.apply({ .apply({
snapshot: false snapshot: false
}); });
this.setState({ state:normalized }); this.setState({ state: normalized });
} }
handleKeyDown(evt) { handleKeyDown(evt) {
if (evt.shiftKey && evt.key === 'Enter') { if (evt.shiftKey && evt.key === 'Enter') {
this.blockEdit = true; this.blockEdit = true;
let { state } = this.state; let { state } = this.state;
state = state state = state
.transform() .transform()
.insertText(' \n') .insertText(' \n')
.apply(); .apply();
this.setState({ state }); this.setState({ state });
} }
@ -300,12 +297,12 @@ class VisualEditor extends React.Component {
return ( return (
<BlockTypesMenu <BlockTypesMenu
isOpen={isOpen} isOpen={isOpen}
plugins={getPlugins()} plugins={getPlugins()}
position={this.menuPositions.blockTypesMenu} position={this.menuPositions.blockTypesMenu}
onClickBlock={this.handleBlockTypeClick} onClickBlock={this.handleBlockTypeClick}
onClickPlugin={this.handlePluginClick} onClickPlugin={this.handlePluginClick}
onClickImage={this.handleImageClick} onClickImage={this.handleImageClick}
/> />
); );
} }
@ -316,14 +313,14 @@ class VisualEditor extends React.Component {
return ( return (
<StylesMenu <StylesMenu
isOpen={isOpen} isOpen={isOpen}
position={this.menuPositions.stylesMenu} position={this.menuPositions.stylesMenu}
marks={this.state.state.marks} marks={this.state.state.marks}
blocks={this.state.state.blocks} blocks={this.state.state.blocks}
inlines={this.state.state.inlines} inlines={this.state.state.inlines}
onClickMark={this.handleMarkStyleClick} onClickMark={this.handleMarkStyleClick}
onClickInline={this.handleInlineClick} onClickInline={this.handleInlineClick}
onClickBlock={this.handleBlockStyleClick} onClickBlock={this.handleBlockStyleClick}
/> />
); );
} }
@ -334,12 +331,12 @@ class VisualEditor extends React.Component {
{this.renderStylesMenu()} {this.renderStylesMenu()}
{this.renderBlockTypesMenu()} {this.renderBlockTypesMenu()}
<Editor <Editor
placeholder={'Enter some rich text...'} placeholder={'Enter some rich text...'}
state={this.state.state} state={this.state.state}
schema={SCHEMA} schema={SCHEMA}
onChange={this.handleChange} onChange={this.handleChange}
onKeyDown={this.handleKeyDown} onKeyDown={this.handleKeyDown}
onDocumentChange={this.handleDocumentChange} onDocumentChange={this.handleDocumentChange}
/> />
</div> </div>
); );
@ -352,5 +349,5 @@ VisualEditor.propTypes = {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onAddMedia: PropTypes.func.isRequired, onAddMedia: PropTypes.func.isRequired,
getMedia: PropTypes.func.isRequired, getMedia: PropTypes.func.isRequired,
value: PropTypes.node, value: PropTypes.string,
}; };

View File

@ -2,31 +2,32 @@ import React, { PropTypes } from 'react';
import { getSyntaxes } from './richText'; import { getSyntaxes } from './richText';
import MarkupItReactRenderer from '../MarkupItReactRenderer/index'; import MarkupItReactRenderer from '../MarkupItReactRenderer/index';
const schema = { const MarkdownPreview = ({ value, getMedia }) => {
'mediaproxy': ({ token }) => (
<img
src={token.getIn(['data', 'src'])}
alt={token.getIn(['data', 'alt'])}
/>
)
};
const MarkdownPreview = ({ value }) => {
if (value == null) { if (value == null) {
return null; return null;
} }
const schema = {
'mediaproxy': ({ token }) => ( // eslint-disable-line
<img
src={getMedia(token.getIn(['data', 'src']))}
alt={token.getIn(['data', 'alt'])}
/>
)
};
const { markdown } = getSyntaxes(); const { markdown } = getSyntaxes();
return ( return (
<MarkupItReactRenderer <MarkupItReactRenderer
value={value} value={value}
syntax={markdown} syntax={markdown}
schema={schema} schema={schema}
/> />
); );
}; };
MarkdownPreview.propTypes = { MarkdownPreview.propTypes = {
getMedia: PropTypes.func.isRequired,
value: PropTypes.string, value: PropTypes.string,
}; };