Add markdown rendering
This commit is contained in:
16
components/md-renderer/index.tsx
Normal file
16
components/md-renderer/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
// @ts-ignore
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import MdxComponents from './mdx-components';
|
||||
|
||||
type MdRendererType = {
|
||||
children: React.ReactNode
|
||||
};
|
||||
|
||||
export default function MdRenderer(props: MdRendererType) {
|
||||
return (
|
||||
<MDXProvider components={MdxComponents}>
|
||||
{props.children}
|
||||
</MDXProvider>
|
||||
);
|
||||
};
|
18
components/md-renderer/mdx-components/a.tsx
Normal file
18
components/md-renderer/mdx-components/a.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { Link } from '@chakra-ui/react';
|
||||
|
||||
type EnrichedLinkProps = {
|
||||
href: string;
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function EnrichedLink(props: EnrichedLinkProps) {
|
||||
// Is external URL or is a media URL
|
||||
const isExternalUrl = /(^http(s)?:\/\/)|(\.(png|svg|jpeg|jpg)$)/.test(props.href);
|
||||
|
||||
return (
|
||||
<Link fontWeight={600} href={props.href} target={isExternalUrl ? '_blank' : '_self'}>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
};
|
22
components/md-renderer/mdx-components/badge-link.tsx
Normal file
22
components/md-renderer/mdx-components/badge-link.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
type BadgeLinkType = {
|
||||
target: string;
|
||||
variant: string;
|
||||
badgeText: string;
|
||||
href: string;
|
||||
children: React.ReactNode
|
||||
};
|
||||
|
||||
export function BadgeLink(props: BadgeLinkType) {
|
||||
const { target = '_blank', variant = 'success', badgeText, href, children } = props;
|
||||
|
||||
return (
|
||||
<p className='mb-0'>
|
||||
<a href={href} target={target}>
|
||||
<span style={{ position: 'relative', top: '-2px' }}
|
||||
className={`badge badge-${variant}`}>{badgeText}</span> {children}
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
}
|
26
components/md-renderer/mdx-components/blockquote.tsx
Normal file
26
components/md-renderer/mdx-components/blockquote.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const BlockQuote = styled.blockquote`
|
||||
padding: 16px 20px;
|
||||
position: relative;
|
||||
background: #e8e8e8;
|
||||
border-radius: 5px;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p + h4 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
|
||||
& + p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default BlockQuote;
|
80
components/md-renderer/mdx-components/heading.tsx
Normal file
80
components/md-renderer/mdx-components/heading.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import LinkIcon from 'components/icons/link.svg';
|
||||
|
||||
const linkify = (Component: React.FunctionComponent<any>) => {
|
||||
return function EnrichedHeading(props: { children: string }): React.ReactNode {
|
||||
const text = props.children;
|
||||
const id = text.toLowerCase && text
|
||||
.toLowerCase()
|
||||
.replace(/[^\x00-\x7F]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[?!]/g, '');
|
||||
|
||||
return (
|
||||
<Component id={id}>
|
||||
<HeaderLink href={`#${id}`}>
|
||||
<LinkIcon />
|
||||
</HeaderLink>
|
||||
{props.children}
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const HeaderLink = styled.a`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -25px;
|
||||
width: 25px;
|
||||
display: none;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
const H1 = styled.h1`
|
||||
position: relative;
|
||||
font-size: 42px;
|
||||
font-weight: 700;
|
||||
margin: 32px 0 10px !important;
|
||||
|
||||
&:hover ${HeaderLink} {
|
||||
display: flex;
|
||||
}
|
||||
`;
|
||||
|
||||
const H2 = styled(H1).attrs({ as: 'h2' })`
|
||||
font-size: 32px;
|
||||
`;
|
||||
|
||||
const H3 = styled(H1).attrs({ as: 'h3' })`
|
||||
margin: 22px 0 8px;
|
||||
font-size: 30px;
|
||||
`;
|
||||
|
||||
const H4 = styled(H1).attrs({ as: 'h4' })`
|
||||
margin: 18px 0 8px;
|
||||
font-size: 24px;
|
||||
`;
|
||||
|
||||
const H5 = styled(H1).attrs({ as: 'h5' })`
|
||||
margin: 14px 0 8px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const H6 = styled(H1).attrs({ as: 'h6' })`
|
||||
margin: 12px 0 8px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const Headings = {
|
||||
h1: linkify(H1),
|
||||
h2: linkify(H2),
|
||||
h3: linkify(H3),
|
||||
h4: linkify(H4),
|
||||
h5: linkify(H5),
|
||||
h6: linkify(H6)
|
||||
};
|
||||
|
||||
export default Headings;
|
8
components/md-renderer/mdx-components/iframe.js
Normal file
8
components/md-renderer/mdx-components/iframe.js
Normal file
@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const IFrame = styled.iframe`
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
margin: 30px auto;
|
||||
`;
|
7
components/md-renderer/mdx-components/img.js
Normal file
7
components/md-renderer/mdx-components/img.js
Normal file
@ -0,0 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Img = styled.img`
|
||||
max-width: 100%;
|
||||
margin: 25px auto;
|
||||
display: block;
|
||||
`;
|
23
components/md-renderer/mdx-components/index.js
Normal file
23
components/md-renderer/mdx-components/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
import P from './p';
|
||||
import Headings from './heading';
|
||||
import { Pre } from './pre';
|
||||
import BlockQuote from './blockquote';
|
||||
import { Table } from './table';
|
||||
import { IFrame } from './iframe';
|
||||
import { Img } from './img';
|
||||
import EnrichedLink from './a';
|
||||
import { BadgeLink } from './badge-link';
|
||||
|
||||
const MdxComponents = {
|
||||
p: P,
|
||||
...Headings,
|
||||
pre: Pre,
|
||||
blockquote: BlockQuote,
|
||||
a: EnrichedLink,
|
||||
table: Table,
|
||||
iframe: IFrame,
|
||||
img: Img,
|
||||
BadgeLink: BadgeLink
|
||||
};
|
||||
|
||||
export default MdxComponents;
|
10
components/md-renderer/mdx-components/p.tsx
Normal file
10
components/md-renderer/mdx-components/p.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Text } from '@chakra-ui/react';
|
||||
|
||||
type EnrichedTextType = {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function EnrichedText(props: EnrichedTextType) {
|
||||
return <Text lineHeight='27px' color='black' mb='18px'>{props.children}</Text>;
|
||||
}
|
12
components/md-renderer/mdx-components/pre.js
Normal file
12
components/md-renderer/mdx-components/pre.js
Normal file
@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Pre = styled.pre`
|
||||
margin: 25px -25px 25px -25px !important;
|
||||
padding: 20px 25px !important;
|
||||
border-radius: 10px;
|
||||
line-height: 1.5 !important;
|
||||
|
||||
code {
|
||||
background: transparent;
|
||||
}
|
||||
`;
|
25
components/md-renderer/mdx-components/table.js
Normal file
25
components/md-renderer/mdx-components/table.js
Normal file
@ -0,0 +1,25 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Table = styled.table`
|
||||
border-collapse: separate;
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
margin: 20px 0;
|
||||
|
||||
th {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
background: #FAFAFA;
|
||||
text-transform: uppercase;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #EAEAEA;
|
||||
}
|
||||
`;
|
Reference in New Issue
Block a user