insomnia/packages/insomnia-app/app/ui/components/markdown-preview.js
Gregory Schier 549ce23ce8
Merge All Repositories into Monorepo for easier maintenance (#629)
* All projects into monorepo

* Update CI

* More CI updates

* Extracted a bunch of things into packages

* Publish

 - insomnia-plugin-base64@1.0.1
 - insomnia-plugin-default-headers@1.0.2
 - insomnia-plugin-file@1.0.1
 - insomnia-plugin-hash@1.0.1
 - insomnia-plugin-now@1.0.1
 - insomnia-plugin-request@1.0.1
 - insomnia-plugin-response@1.0.1
 - insomnia-plugin-uuid@1.0.1
 - insomnia-cookies@0.0.2
 - insomnia-importers@1.5.2
 - insomnia-prettify@0.0.3
 - insomnia-url@0.0.2
 - insomnia-xpath@0.0.2

* A bunch of small fixes

* Improved build script

* Fixed

* Merge dangling files

* Usability refactor

* Handle duplicate plugin names
2017-11-26 20:45:40 +00:00

121 lines
2.9 KiB
JavaScript

import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import classnames from 'classnames';
import autobind from 'autobind-decorator';
import highlight from 'highlight.js';
import * as misc from '../../common/misc';
import {markdownToHTML} from '../../common/markdown-to-html';
@autobind
class MarkdownPreview extends PureComponent {
constructor (props) {
super(props);
this.state = {
compiled: '',
renderError: ''
};
}
/**
* Debounce and compile the markdown (won't debounce first render)
*/
_compileMarkdown (markdown) {
clearTimeout(this._compileTimeout);
this._compileTimeout = setTimeout(async () => {
try {
const rendered = await this.props.handleRender(markdown);
this.setState({
compiled: markdownToHTML(rendered),
renderError: ''
});
} catch (err) {
this.setState({
renderError: err.message,
compiled: ''
});
}
}, this.state.compiled ? this.props.debounceMillis : 0);
}
_setPreviewRef (n) {
this._preview = n;
}
_handleClickLink (e) {
e.preventDefault();
misc.clickLink(e.target.getAttribute('href'));
}
_highlightCodeBlocks () {
if (!this._preview) {
return;
}
const el = ReactDOM.findDOMNode(this._preview);
for (const block of el.querySelectorAll('pre > code')) {
highlight.highlightBlock(block);
}
for (const a of el.querySelectorAll('a')) {
a.title = `Open ${a.getAttribute('href')} in browser`;
a.removeEventListener('click', this._handleClickLink);
a.addEventListener('click', this._handleClickLink);
}
}
componentWillUnmount () {
clearTimeout(this._compileTimeout);
}
componentWillMount () {
this._compileMarkdown(this.props.markdown);
}
componentWillReceiveProps (nextProps) {
this._compileMarkdown(nextProps.markdown);
}
componentDidUpdate () {
this._highlightCodeBlocks();
}
componentDidMount () {
this._highlightCodeBlocks();
}
render () {
const {className, heading} = this.props;
const {compiled, renderError} = this.state;
let html = heading ? `<h1>${heading}</h1>\n${compiled}` : compiled;
return (
<div ref={this._setPreviewRef} className={classnames('markdown-preview', className)}>
{renderError && (
<p className="notice error no-margin">
Failed to render: {renderError}
</p>
)}
<div className="markdown-preview__content selectable"
dangerouslySetInnerHTML={{__html: html}}>
{/* Set from above */}
</div>
</div>
);
}
}
MarkdownPreview.propTypes = {
// Required
markdown: PropTypes.string.isRequired,
handleRender: PropTypes.func.isRequired,
// Optional
className: PropTypes.string,
debounceMillis: PropTypes.number,
heading: PropTypes.string
};
export default MarkdownPreview;