UNPKG

materialuiupgraded

Version:

Material-UI's workspace package

275 lines (257 loc) 7.96 kB
import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import marked from 'marked'; import { withStyles } from '@material-ui/core/styles'; import prism from './prism'; // Monkey patch to preserve non-breaking spaces // https://github.com/chjj/marked/blob/6b0416d10910702f73da9cb6bb3d4c8dcb7dead7/lib/marked.js#L142-L150 marked.Lexer.prototype.lex = function lex(src) { src = src .replace(/\r\n|\r/g, '\n') .replace(/\t/g, ' ') .replace(/\u2424/g, '\n'); return this.token(src, true); }; const renderer = new marked.Renderer(); export function textToHash(text) { return text .toLowerCase() .replace(/=&gt;|&lt;| \/&gt;|<code>|<\/code>/g, '') .replace(/\W/g, '-'); } renderer.heading = (text, level) => { // Small title. No need for an anchor. // It's reducing the risk of duplicated id and it's fewer elements in the DOM. if (level >= 4) { return `<h${level}>${text}</h${level}>`; } const escapedText = textToHash(text); return ( ` <h${level}> <a class="anchor-link" id="${escapedText}"></a>${text}` + `<a class="anchor-link-style" href="#${escapedText}"> <svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><path d="M46.9 13.9c-.5-.6-1.2-.94-2.07-.94h-6.67l1.86-8.98c.17-.85 0-1.7-.52-2.3-.48-.6-1.2-.94-2.07-.94-1.6 0-3.2 1.27-3.54 2.93l-.5 2.42c0 .07-.07.13-.07.2l-1.37 6.62H20.7l1.88-8.96c.16-.85 0-1.7-.53-2.3-.48-.6-1.2-.94-2.07-.94-1.65 0-3.2 1.27-3.56 2.93l-.52 2.58v.08l-1.37 6.64H7.3c-1.67 0-3.22 1.3-3.58 2.96-.16.86 0 1.7.52 2.3.48.6 1.2.93 2.07.93h6.97l-2 9.65H4c-1.67 0-3.22 1.27-3.56 2.94-.2.8 0 1.67.5 2.27.5.6 1.2.93 2.08.93H10l-1.84 9.05c-.2.84 0 1.67.52 2.3.5.6 1.25.92 2.08.92 1.66 0 3.2-1.3 3.55-2.94l1.94-9.33h11.22l-1.87 9.05c-.15.84.03 1.67.53 2.3.5.6 1.2.92 2.07.92 1.65 0 3.22-1.3 3.56-2.94l1.9-9.33h7c1.6 0 3.2-1.28 3.53-2.93.2-.87 0-1.7-.52-2.3-.48-.62-1.2-.96-2.05-.96h-6.7l2.02-9.65h6.93c1.67 0 3.22-1.27 3.56-2.92.2-.85 0-1.7-.5-2.3l-.04.03zM17.53 28.77l1.95-9.65H30.7l-1.97 9.66H17.5h.03z"/></svg> </a></h${level}> ` ); }; const markedOptions = { gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false, highlight(code, lang) { let language; switch (lang) { case 'diff': language = prism.languages.diff; break; case 'css': language = prism.languages.css; break; case 'ts': case 'tsx': language = prism.languages.typescript; break; case 'js': case 'jsx': default: language = prism.languages.jsx; break; } return prism.highlight(code, language); }, renderer, }; const styles = theme => ({ root: { fontFamily: theme.typography.fontFamily, fontSize: 16, color: theme.palette.text.primary, '& .anchor-link': { marginTop: -96, // Offset for the anchor. position: 'absolute', }, '& pre, & pre[class*="language-"]': { margin: '24px 0', padding: '12px 18px', backgroundColor: theme.palette.background.paper, borderRadius: theme.shape.borderRadius, overflow: 'auto', WebkitOverflowScrolling: 'touch', // iOS momentum scrolling. }, '& code': { display: 'inline-block', lineHeight: 1.6, fontFamily: 'Consolas, "Liberation Mono", Menlo, Courier, monospace', padding: '3px 6px', color: theme.palette.text.primary, backgroundColor: theme.palette.background.paper, fontSize: 14, }, '& p code, & ul code, & pre code': { fontSize: 14, lineHeight: 1.6, }, '& h1': { ...theme.typography.h2, margin: '32px 0 16px', }, '& .description': { ...theme.typography.h5, margin: '0 0 40px', }, '& h2': { ...theme.typography.h4, margin: '32px 0 24px', }, '& h3': { ...theme.typography.h5, margin: '32px 0 24px', }, '& h4': { ...theme.typography.h6, margin: '24px 0 16px', }, '& p, & ul, & ol': { lineHeight: 1.6, }, '& h1, & h2, & h3, & h4': { '& code': { fontSize: 'inherit', lineHeight: 'inherit', // Remove scroll on small screens. wordBreak: 'break-word', }, '& .anchor-link-style': { opacity: 0, // To prevent the link to get the focus. display: 'none', }, '&:hover .anchor-link-style': { display: 'inline-block', opacity: 1, padding: '0 8px', color: theme.palette.text.hint, '&:hover': { color: theme.palette.text.secondary, }, '& svg': { width: '0.55em', height: '0.55em', fill: 'currentColor', }, }, }, '& table': { width: '100%', display: 'block', overflowX: 'auto', WebkitOverflowScrolling: 'touch', // iOS momentum scrolling. borderCollapse: 'collapse', borderSpacing: 0, overflow: 'hidden', '& .prop-name': { fontSize: 13, fontFamily: 'Consolas, "Liberation Mono", Menlo, monospace', }, '& .required': { color: theme.palette.type === 'light' ? '#006500' : '#9bc89b', }, '& .prop-type': { fontSize: 13, fontFamily: 'Consolas, "Liberation Mono", Menlo, monospace', color: theme.palette.type === 'light' ? '#932981' : '#dbb0d0', }, '& .prop-default': { fontSize: 13, fontFamily: 'Consolas, "Liberation Mono", Menlo, monospace', borderBottom: `1px dotted ${theme.palette.text.hint}`, }, }, '& thead': { fontSize: 14, fontWeight: theme.typography.fontWeightMedium, color: theme.palette.text.secondary, }, '& tbody': { fontSize: 14, lineHeight: 1.5, color: theme.palette.text.primary, }, '& td': { borderBottom: `1px solid ${theme.palette.divider}`, padding: '8px 16px 8px 8px', textAlign: 'left', }, '& td:last-child': { paddingRight: 24, }, '& td compact': { paddingRight: 24, }, '& td code': { fontSize: 13, lineHeight: 1.6, }, '& th': { whiteSpace: 'pre', borderBottom: `1px solid ${theme.palette.divider}`, fontWeight: theme.typography.fontWeightMedium, padding: '0 16px 0 8px', textAlign: 'left', }, '& th:last-child': { paddingRight: 24, }, '& tr': { height: 48, }, '& thead tr': { height: 64, }, '& strong': { fontWeight: theme.typography.fontWeightMedium, }, '& blockquote': { borderLeft: `5px solid ${theme.palette.text.hint}`, backgroundColor: theme.palette.background.paper, padding: '4px 24px', margin: '24px 0', }, '& a, & a code': { // Style taken from the Link component color: theme.palette.secondary.main, textDecoration: 'none', '&:hover': { textDecoration: 'underline', }, }, '& img': { maxWidth: '100%', }, }, }); function MarkdownElement(props) { const { classes, className, text, ...other } = props; /* eslint-disable react/no-danger */ return ( <div className={classNames(classes.root, 'markdown-body', className)} dangerouslySetInnerHTML={{ __html: marked(text, markedOptions) }} {...other} /> ); /* eslint-enable */ } MarkdownElement.propTypes = { classes: PropTypes.object.isRequired, className: PropTypes.string, text: PropTypes.string, }; export default withStyles(styles, { flip: false })(MarkdownElement);