Skip to content

Commit

Permalink
feat: mdx code add parameters and mdx docs
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Jun 28, 2020
1 parent c87e3b5 commit 58f80ba
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 54 deletions.
2 changes: 1 addition & 1 deletion core/instrument/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ _Defined in [@types/resolve/index.d.ts](https://github.com/DefinitelyTyped/Defin

enable footnotes

**mdPlugins**? : _any\[]_
**remarkPlugins**? : _any\[]_

specify remark plugins

Expand Down
4 changes: 2 additions & 2 deletions core/instrument/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { mdx } from '@mdx-js/react';
export const defaultMDXOptions: MDXOptions = {
test: /\.(mdx|md)$/i,
renderer: DEFAULT_MDX_RENDERER,
mdPlugins: [images, emoji],
remarkPlugins: [images, emoji],
};

/**
Expand Down Expand Up @@ -169,7 +169,7 @@ export interface MDXOptions {
/**
* specify remark plugins
*/
mdPlugins?: any[];
remarkPlugins?: any[];
/**
* specify rehype plugins
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ Object {
"copy-to-clipboard": "^3.2.1",
"fast-memoize": "^2.5.2",
"markdown-to-jsx": "^6.11.0",
"mdx-utils": "*",
"prism-react-renderer": "^1.0.2",
"react": "^16.13.1",
"react-animate-height": "^2.0.20",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export const ActionBar: FC<ActionBarProps> = ({
"copy-to-clipboard": "^3.2.1",
"fast-memoize": "^2.5.2",
"markdown-to-jsx": "^6.11.0",
"mdx-utils": "*",
"prism-react-renderer": "^1.0.2",
"react": "^16.13.1",
"react-animate-height": "^2.0.20",
Expand Down Expand Up @@ -242,6 +243,7 @@ export const ActionBar: FC<ActionBarProps> = ({
"copy-to-clipboard": "^3.2.1",
"fast-memoize": "^2.5.2",
"markdown-to-jsx": "^6.11.0",
"mdx-utils": "*",
"prism-react-renderer": "^1.0.2",
"react": "^16.13.1",
"react-animate-height": "^2.0.20",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ order: 0

In MDX, you can import any react components and use them to create rich documentation pages:

```jsx
```mdx
---
title: My Button
---
Expand All @@ -53,11 +53,58 @@ import { Button } from 'theme-ui';

MDX allows you to import external MDX and MD files statically and display their content:

```jsx
```mdx
---
title: Test Transclusion
---
import Transclusion from '../sections/transclusion.mdx';

<Transclusion />
```
```

# Syntax highlighting

You can include source code in your MDX and MD documentation and add some useful parameters

## Language

The language can be specified as the first parameter
````mdx
```jsx
import { Button } from 'theme-ui';
```
````

## Title

A title attribute can be added. Due to some current MDX limitations, the title can not contain spaces.

````mdx:title=my-title
```jsx:title=my-title
import { Button } from 'theme-ui';
```
````

## Highlight lines

You can specify a line to highlight
````markdown {2}
```jsx {2}
import { Button } from 'theme-ui';
<Button >click me </Button>
```
````

Or a range of lines
````markdown {2-3}
```jsx {2-3}
import { Button } from 'theme-ui';
<Button >click me </Button>
```
````

## Emoji

You can use `:emoji:` in your documents

`:rocket: :dog: :+1:` = :rocket: :dog: :+1:
1 change: 1 addition & 0 deletions ui/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@theme-ui/presets": "next",
"copy-to-clipboard": "^3.2.1",
"fast-memoize": "^2.5.2",
"mdx-utils": "*",
"markdown-to-jsx": "^6.11.0",
"prism-react-renderer": "^1.0.2",
"react": "^16.13.1",
Expand Down
69 changes: 46 additions & 23 deletions ui/components/src/Markdown/MarkdownComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
/* eslint-disable react/display-name */
/** @jsx jsx */
import { ComponentType } from 'react';
import { preToCodeBlock } from 'mdx-utils';
import { jsx } from 'theme-ui';
import {
Label,
Flex,
Box,
Heading,
Button,
Card,
Image,
Avatar,
} from 'theme-ui';
import { Label, Button, Image } from 'theme-ui';
import { Language } from 'prism-react-renderer';
import { SyntaxHighlighter } from '../SyntaxHighlighter';
import { Source } from '../Source';
Expand All @@ -27,6 +19,7 @@ const mdxLanguageMap: MDXLanguageType = {
css: 'css',
js: 'javascript',
jsx: 'jsx',
JSX: 'jsx',
'coffee-script': 'coffeescript',
coffeescript: 'coffeescript',
coffee: 'coffeescript',
Expand All @@ -38,6 +31,7 @@ const mdxLanguageMap: MDXLanguageType = {
make: 'makefile',
Makefile: 'makefile',
markdown: 'markdown',
mdx: 'jsx',
objectivec: 'objectivec',
python: 'python',
scss: 'scss',
Expand All @@ -47,26 +41,55 @@ const mdxLanguageMap: MDXLanguageType = {
export interface MarkdownComponentType {
[key: string]: ComponentType<any>;
}
const paramsFromClassName = (className: string = ``) => {
const [lang = ``, params = ``] = className.split(`:`);

return [
// @ts-ignore
lang
.split(`language-`)
.pop()
.split(`{`)
.shift(),
].concat(
// @ts-ignore
params.split(`&`).reduce((merged, param) => {
const [key, value] = param.split(`=`);
// @ts-ignore
merged[key] = value;
return merged;
}, {}),
);
};
export const markdownComponents: MarkdownComponentType = {
code: props => {
return <SyntaxHighlighter {...props} />;
},
pre: props => {
const codeProps = props?.children?.props?.children
? props.children.props
: props;
const { className = '', children } = codeProps || {};
const arrClass = className.split('-');
const mdxLanguage = arrClass.length === 2 ? arrClass[1] : 'js';
const language = mdxLanguageMap[mdxLanguage] || mdxLanguage;
return <Source language={language}>{children}</Source>;
const mdxProps = preToCodeBlock(props);
if (!mdxProps) {
return <pre {...props} />;
}
const { codeString = '', metastring, className } = mdxProps;
const [language = 'jsx', ...rest] = paramsFromClassName(className);
const otherProps = Array.isArray(rest)
? rest.reduce(
(acc, p) =>
typeof p === 'object' ? { ...acc, ...(p as object) } : acc,
{},
)
: undefined;
return (
<Source
language={mdxLanguageMap[language] || language}
metastring={metastring}
{...otherProps}
>
{codeString.trimRight()}
</Source>
);
},
avatar: Avatar,
image: Image,
box: Box,
button: Button,
card: Card,
flex: Flex,
heading: Heading,
label: Label,
};
2 changes: 1 addition & 1 deletion ui/components/src/Source/Source.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const Source: FC<SourceProps> = ({
display: 'block',
}}
>
{children.trimRight()}
{children}
</SyntaxHighlighter>
</ActionContainer>
);
Expand Down
97 changes: 73 additions & 24 deletions ui/components/src/SyntaxHighlighter/SyntaxHighlighter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @jsx jsx */
/* eslint react/jsx-key: 0 */
import { jsx } from 'theme-ui';
import React, { FC } from 'react';
import { jsx, Heading } from 'theme-ui';
import { FC, Fragment } from 'react';
import { Styled, Box, useColorMode } from 'theme-ui';
import Highlight, {
defaultProps,
Expand All @@ -13,6 +13,26 @@ import duotoneLight from 'prism-react-renderer/themes/duotoneLight';

type RenderProps = Parameters<Highlight['props']['children']>[0];

// from lekoarts gatsby themes
// https://github.com/LekoArts/gatsby-themes/blob/master/themes/gatsby-theme-minimal-blog/src/components/code.tsx#L34
const RE = /{([\d,-]+)}/;

const calculateLinesToHighlight = (meta: string) => {
if (!RE.test(meta)) {
return () => false;
}
const lineNumbers = RE.exec(meta)![1]
.split(`,`)
.map(v => v.split(`-`).map(x => parseInt(x, 10)));
return (index: number) => {
const lineNumber = index + 1;
const inRange = lineNumbers.some(([start, end]) =>
end ? lineNumber >= start && lineNumber <= end : lineNumber === start,
);
return inRange;
};
};

export interface SyntaxHighlighterProps {
/**
* source code to be displayed.
Expand All @@ -22,6 +42,12 @@ export interface SyntaxHighlighterProps {
* optional `PrismTheme` theme provided to the component. Themes can be imported from `prism-react-renderer/themes`.
*/
theme?: PrismTheme;

/**
* optional title to display for the code block. Usually used from MDX
*/
title?: string;

/**
* source lnguage used, by default "jsx".
*/
Expand Down Expand Up @@ -49,6 +75,11 @@ export interface SyntaxHighlighterProps {
* syntax container as element. Can be used as `div` or `span`.
*/
as?: React.ElementType;

/**
* code configuration string passed from MDX
*/
metastring?: string;
}

/**
Expand All @@ -61,37 +92,55 @@ export const SyntaxHighlighter: FC<SyntaxHighlighterProps> = ({
renderFn,
dark = false,
style: propStyle,
title,
metastring = ``,
as = 'span',
}) => {
const [colorMode] = useColorMode();
const isDark = dark === true || colorMode === `dark`;
const theme = customTheme ? customTheme : isDark ? duotoneDark : duotoneLight;

const shouldHighlightLine = calculateLinesToHighlight(metastring);
const renderProps =
typeof renderFn === 'function'
? (props: RenderProps) => renderFn(props, { theme })
: ({ className, style, tokens, getLineProps, getTokenProps }: any) => (
<Styled.pre
className={`${className}`}
style={{
...style,
padding: '3px 5px',
display: 'inline',
margin: 0,
...propStyle,
}}
>
{tokens.map((line: string[], i: number) => (
<Box as={as} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span
{...getTokenProps({ token, key })}
sx={{ display: 'inline-block' }}
/>
))}
</Box>
))}
</Styled.pre>
<Fragment>
{title && (
<Heading as="h2" variant="syntaxhighlight.title">
{title}
</Heading>
)}
<Styled.pre
className={`${className}`}
style={{
...style,
padding: '3px 5px',
display: 'inline',
margin: 0,
...propStyle,
}}
>
{tokens.map((line: string[], i: number) => {
const highlight = shouldHighlightLine(i);
return (
<Box
as={as}
variant={`syntaxhighlight.${
highlight ? 'highlight' : 'normal'
}`}
{...getLineProps({ line, key: i })}
>
{line.map((token, key) => (
<span
{...getTokenProps({ token, key })}
sx={{ display: 'inline-block' }}
/>
))}
</Box>
);
})}
</Styled.pre>
</Fragment>
);
const props = { ...defaultProps, theme };

Expand Down
Loading

0 comments on commit 58f80ba

Please sign in to comment.