2018-03-26 11:14:07 +02:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* PreviewWysiwyg
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
import { CompositeDecorator, ContentState, convertFromHTML, EditorState } from 'draft-js';
|
|
|
|
|
import cn from 'classnames';
|
|
|
|
|
import { isEmpty } from 'lodash';
|
|
|
|
|
|
|
|
|
|
import WysiwygEditor from 'components/WysiwygEditor';
|
2018-03-28 16:00:08 +02:00
|
|
|
import converter from './converter';
|
2018-03-30 12:06:37 +02:00
|
|
|
import { findLinkEntities, findImageEntities, findVideoEntities } from './strategies';
|
2018-03-26 11:14:07 +02:00
|
|
|
|
2018-03-28 16:00:08 +02:00
|
|
|
import Image from './image';
|
|
|
|
|
import Link from './link';
|
2018-03-30 12:06:37 +02:00
|
|
|
import Video from './video';
|
|
|
|
|
|
2018-03-28 16:00:08 +02:00
|
|
|
import styles from './componentsStyles.scss';
|
2018-03-26 11:14:07 +02:00
|
|
|
|
2018-03-26 16:28:21 +02:00
|
|
|
function getBlockStyle(block) {
|
|
|
|
|
switch (block.getType()) {
|
|
|
|
|
case 'blockquote':
|
|
|
|
|
return styles.editorBlockquote;
|
|
|
|
|
case 'code-block':
|
|
|
|
|
return styles.editorCodeBlock;
|
|
|
|
|
case 'unstyled':
|
|
|
|
|
return styles.editorParagraph;
|
|
|
|
|
case 'unordered-list-item':
|
|
|
|
|
return styles.unorderedList;
|
|
|
|
|
case 'ordered-list-item':
|
|
|
|
|
case 'header-one':
|
|
|
|
|
case 'header-two':
|
|
|
|
|
case 'header-three':
|
|
|
|
|
case 'header-four':
|
|
|
|
|
case 'header-five':
|
|
|
|
|
case 'header-six':
|
|
|
|
|
default:
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-09 15:52:58 +02:00
|
|
|
const decorator = new CompositeDecorator([
|
|
|
|
|
{
|
|
|
|
|
strategy: findLinkEntities,
|
|
|
|
|
component: Link,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
strategy: findImageEntities,
|
|
|
|
|
component: Image,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
strategy: findVideoEntities,
|
|
|
|
|
component: Video,
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
2018-04-09 16:41:34 +02:00
|
|
|
class PreviewWysiwyg extends React.PureComponent {
|
2018-04-09 15:52:58 +02:00
|
|
|
state = { editorState: EditorState.createEmpty(), isMounted: false };
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
const { data } = this.props;
|
|
|
|
|
this.setState({ isMounted: true });
|
|
|
|
|
|
|
|
|
|
if (!isEmpty(data)) {
|
|
|
|
|
this.previewHTML(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
|
this.setState({ isMounted: false });
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-26 11:14:07 +02:00
|
|
|
getClassName = () => {
|
|
|
|
|
if (this.context.isFullscreen) {
|
|
|
|
|
return cn(styles.editor, styles.editorFullScreen, styles.fullscreenPreviewEditor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return styles.editor;
|
|
|
|
|
};
|
|
|
|
|
|
2018-04-10 10:16:37 +02:00
|
|
|
// NOTE: This is not optimal and this lifecycle should be removed
|
2018-04-11 12:40:29 +02:00
|
|
|
// I couldn't find a better way to decrease the fullscreen preview's data conversion time
|
|
|
|
|
// Trying with componentDidUpdate didn't work
|
2018-04-09 15:52:58 +02:00
|
|
|
UNSAFE_componentWillUpdate(nextProps, nextState) {
|
|
|
|
|
if (nextProps.data !== this.props.data) {
|
|
|
|
|
new Promise(resolve => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (nextProps.data === this.props.data && nextState.isMounted) {
|
2018-04-11 12:40:29 +02:00
|
|
|
// I use an handler here to update the state wich is fine since the condition above prevent
|
|
|
|
|
// from entering into an infinite loop
|
2018-04-09 15:52:58 +02:00
|
|
|
this.previewHTML(nextProps.data);
|
|
|
|
|
}
|
|
|
|
|
resolve();
|
|
|
|
|
}, 300);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-26 16:28:21 +02:00
|
|
|
|
2018-04-09 16:47:31 +02:00
|
|
|
previewHTML = rawContent => {
|
2018-04-09 15:52:58 +02:00
|
|
|
const initHtml = isEmpty(rawContent) ? '<p></p>' : rawContent;
|
|
|
|
|
const html = converter.makeHtml(initHtml);
|
2018-04-11 12:40:29 +02:00
|
|
|
// This action takes a long time
|
2018-03-26 11:14:07 +02:00
|
|
|
const blocksFromHTML = convertFromHTML(html);
|
2018-04-09 15:52:58 +02:00
|
|
|
|
2018-03-26 11:14:07 +02:00
|
|
|
// Make sure blocksFromHTML.contentBlocks !== null
|
|
|
|
|
if (blocksFromHTML.contentBlocks) {
|
|
|
|
|
const contentState = ContentState.createFromBlockArray(
|
|
|
|
|
blocksFromHTML.contentBlocks,
|
|
|
|
|
blocksFromHTML.entityMap,
|
|
|
|
|
);
|
2018-04-09 15:52:58 +02:00
|
|
|
return this.setState({ editorState: EditorState.createWithContent(contentState, decorator) });
|
2018-03-26 11:14:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prevent errors if value is empty
|
2018-04-09 15:52:58 +02:00
|
|
|
return this.setState({ editorState: EditorState.createEmpty() });
|
2018-03-26 11:14:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
const { placeholder } = this.context;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={this.getClassName()}>
|
|
|
|
|
<WysiwygEditor
|
|
|
|
|
blockStyleFn={getBlockStyle}
|
2018-04-09 15:52:58 +02:00
|
|
|
editorState={this.state.editorState}
|
2018-03-26 11:14:07 +02:00
|
|
|
onChange={() => {}}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
/>
|
|
|
|
|
<input className={styles.editorInput} value="" tabIndex="-1" />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PreviewWysiwyg.contextTypes = {
|
|
|
|
|
isFullscreen: PropTypes.bool,
|
|
|
|
|
placeholder: PropTypes.string,
|
|
|
|
|
};
|
|
|
|
|
|
2018-04-09 15:52:58 +02:00
|
|
|
PreviewWysiwyg.defaultProps = {
|
|
|
|
|
data: '',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PreviewWysiwyg.propTypes = {
|
|
|
|
|
data: PropTypes.string,
|
|
|
|
|
};
|
|
|
|
|
|
2018-03-26 11:14:07 +02:00
|
|
|
export default PreviewWysiwyg;
|