add inline image support for editor

This commit is contained in:
Shawn Erquhart 2017-09-19 16:49:45 -04:00
parent e937e8e626
commit 70a4a51b97
5 changed files with 64 additions and 11 deletions

View File

@ -35,7 +35,6 @@ export const NODE_COMPONENTS = {
'numbered-list': props => 'numbered-list': props =>
<ol {...props.attributes} start={props.node.data.get('start') || 1}>{props.children}</ol>, <ol {...props.attributes} start={props.node.data.get('start') || 1}>{props.children}</ol>,
'link': props => { 'link': props => {
// Need to wrap this in mark components for any marks found in data.
const data = props.node.get('data'); const data = props.node.get('data');
const marks = data.get('marks'); const marks = data.get('marks');
const url = data.get('url'); const url = data.get('url');
@ -47,6 +46,19 @@ export const NODE_COMPONENTS = {
}, link); }, link);
return result; return result;
}, },
'image': props => {
const data = props.node.get('data');
const marks = data.get('marks');
const url = data.get('url');
const title = data.get('title');
const alt = data.get('alt');
const image = <img src={url} title={title} alt={alt} {...props.attributes}/>;
const result = !marks ? image : marks.reduce((acc, mark) => {
const MarkComponent = MARK_COMPONENTS[mark.type];
return <MarkComponent>{acc}</MarkComponent>;
}, image);
return result;
},
'shortcode': props => { 'shortcode': props => {
const { attributes, node, state: editorState } = props; const { attributes, node, state: editorState } = props;
const isSelected = editorState.selection.hasFocusIn(node); const isSelected = editorState.selection.hasFocusIn(node);

View File

@ -28,4 +28,8 @@ describe('slate', () => {
expect(process('**a**b**c**')).toEqual('**a**b**c**\n'); expect(process('**a**b**c**')).toEqual('**a**b**c**\n');
expect(process('**a _b_ c**')).toEqual('**a _b_ c**\n'); expect(process('**a _b_ c**')).toEqual('**a _b_ c**\n');
}); });
it('should parse inline images as images', () => {
expect(process('a ![b](c)')).toEqual('a ![b](c)\n');
});
}); });

View File

@ -1,18 +1,26 @@
/** /**
* Images must be parsed as shortcodes for asset proxying. This plugin converts * Images must be parsed as shortcodes for asset proxying. This plugin converts
* MDAST image nodes back to text to allow shortcode pattern matching. * MDAST image nodes back to text to allow shortcode pattern matching. Note that
* this transformation only occurs for images that are the sole child of a top
* level paragraph - any other image is left alone and treated as an inline
* image.
*/ */
export default function remarkImagesToText() { export default function remarkImagesToText() {
return transform; return transform;
function transform(node) { function transform(node) {
const children = node.children ? node.children.map(transform) : node.children; const children = node.children.map(child => {
if (node.type === 'image') { if (
const alt = node.alt || ''; child.type === 'paragraph'
const url = node.url || ''; && child.children.length === 1
const title = node.title ? ` "${node.title}"` : ''; && child.children[0].type === 'image'
return { type: 'text', value: `![${alt}](${url}${title})` }; ) {
} const { alt = '', url = '', title = '' } = child.children[0];
const value = `![${alt}](${url}${title ? ' title' : ''})`;
child.children = [{ type: 'text', value }];
}
return child;
});
return { ...node, children }; return { ...node, children };
} }
} }

View File

@ -74,7 +74,7 @@ function createBlock(type, nodes, props = {}) {
/** /**
* Create a Slate Block node. * Create a Slate Block node.
*/ */
function createInline(type, nodes, props = {}) { function createInline(type, props = {}, nodes) {
return { kind: 'inline', type, nodes, ...props }; return { kind: 'inline', type, nodes, ...props };
} }
@ -312,9 +312,23 @@ function convertNode(node, nodes) {
case 'link': { case 'link': {
const { title, url, data } = node; const { title, url, data } = node;
const newData = { ...data, title, url }; const newData = { ...data, title, url };
return createInline(typeMap[type], nodes, { data: newData }); return createInline(typeMap[type], { data: newData }, nodes);
} }
/**
* Images
*
* Identical to link nodes except for the lack of child nodes and addition
* of alt attribute data MDAST stores the link attributes directly on the
* node, while our Slate schema references them in the data object.
*/
case 'image': {
const { title, url, alt, data } = node;
const newData = { ...data, title, alt, url };
return createInline(typeMap[type], { isVoid: true, data: newData });
}
/** /**
* Tables * Tables
* *

View File

@ -474,6 +474,21 @@ function convertNode(node, children, shortcodePlugins) {
return u(typeMap[node.type], { url, title, data }, children); return u(typeMap[node.type], { url, title, data }, children);
} }
/**
* Images
*
* This transformation is almost identical to that of links, except for the
* lack of child nodes and addition of `alt` attribute data. Currently the
* CMS handles block images by shortcode, so this case will only apply to
* inline images, which currently can only occur through raw markdown
* insertion.
*/
case 'image': {
const { url, title, alt, ...data } = get(node, 'data', {});
return u(typeMap[node.type], { url, title, alt, data });
}
/** /**
* No default case is supplied because an unhandled case should never * No default case is supplied because an unhandled case should never
* occur. In the event that it does, let the error throw (for now). * occur. In the event that it does, let the error throw (for now).