add inline image support for editor
This commit is contained in:
parent
e937e8e626
commit
70a4a51b97
@ -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);
|
||||||
|
@ -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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user