Skip to content

Commit

Permalink
Merge pull request #100 from ant-design/feat/Markdown
Browse files Browse the repository at this point in the history
✨ feat: add Markdown new Components
  • Loading branch information
rdmclin2 authored Nov 22, 2023
2 parents 1d919e5 + ea44085 commit c2a241e
Show file tree
Hide file tree
Showing 13 changed files with 3,336 additions and 320 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,12 @@
"react-copy-to-clipboard": "^5.1.0",
"react-hotkeys-hook": "^4.4.1",
"react-layout-kit": "^1.7.1",
"react-markdown": "^8.0.1",
"react-rnd": "^10.4.1",
"reactflow": "^11.8.3",
"rehype-katex": "^6.0.0",
"remark-gfm": "^3.0.0",
"remark-math": "^5.0.0",
"rxjs": "^7.8.1",
"shikiji": "^0.6.12",
"type-fest": "^3.13.1",
Expand Down
2 changes: 1 addition & 1 deletion src/Highlight/defalut.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface HighlightProps {
* @renderType select
* @default "typescript"
*/
language: string;
language?: string;
/**
* @title 主题
* @description 主题颜色, dark 黑色主题,light 白色主题
Expand Down
30 changes: 26 additions & 4 deletions src/Highlight/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import { ConfigProvider } from '../ConfigProvider';
import { HighlightBase, HighlightProps } from './defalut';
import { useStyles } from './style';
import FullFeatureWrapper from './wrapper';

const Highlight = (props: HighlightProps) => {
if (props?.containerWrapper) {
return <FullFeatureWrapper {...props} />;
}
return <HighlightBase {...props} />;
const { prefixCls, type, theme, containerWrapper } = props;
const { theme: token } = useStyles({
prefixCls,
type,
theme,
});

return (
<ConfigProvider
componentToken={{
Select: {
colorBgTextHover: token.colorFillSecondary,
colorBgTextActive: token.colorFill,
},
Button: {
colorText: token.colorTextTertiary,
colorBgTextHover: token.colorFillSecondary,
colorBgTextActive: token.colorFill,
},
}}
>
{containerWrapper ? <FullFeatureWrapper {...props} /> : <HighlightBase {...props} />}
</ConfigProvider>
);
};

export * from './defalut';
Expand Down
8 changes: 0 additions & 8 deletions src/Highlight/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,6 @@ export const useStyles = createStyles(
transition: opacity 0.1s;
`,
),
expandIcon: css`
color: ${colorText};
&:hover {
.${STUDIO_UI_PREFIX}-btn-icon {
color: ${colorText} !important;
}
}
`,
select: css`
min-width: 100px;
.${STUDIO_UI_PREFIX}-btn {
Expand Down
11 changes: 5 additions & 6 deletions src/Highlight/wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import CopyButton from '@/components/CopyButton';
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { ActionIcon, Button, Select, type SelectProps } from '@ant-design/pro-editor';
import classNames from 'classnames';
import { memo, useState } from 'react';
import { DivProps, Flexbox } from 'react-layout-kit';
import { getPrefixCls } from '..';
import CopyButton from './components/CopyButton';
import { HighlightBase, HighlightProps } from './defalut';
import { languageMap } from './hooks/useHighlight';
import { useStyles } from './style';
Expand All @@ -18,7 +18,7 @@ export interface HighlighterWrapperProps extends DivProps {
/**
* @description The language of the code content
*/
language: string;
language?: string;
}
const options: SelectProps['options'] = Object.keys(languageMap).map((key) => ({
label: key,
Expand Down Expand Up @@ -46,10 +46,9 @@ export const FullFeatureWrapper = memo<HighlighterWrapperProps & HighlightProps>
});

return (
<div className={classNames(styles.wrapper, className)} style={style} {...props}>
<div className={classNames(styles.wrapper, className)} style={style}>
<Flexbox align={'center'} className={styles.header} horizontal justify={'space-between'}>
<ActionIcon
className={styles.expandIcon}
icon={expand ? <DownOutlined size={14} /> : <RightOutlined size={14} />}
onClick={() => setExpand(!expand)}
size={24}
Expand All @@ -74,12 +73,12 @@ export const FullFeatureWrapper = memo<HighlighterWrapperProps & HighlightProps>
suffixIcon={false}
value={lang.toLowerCase()}
/>
{copyable && <CopyButton content={children} prefixCls={prefixCls} theme={theme} />}
{copyable && <CopyButton content={children} />}
</Flexbox>
<HighlightBase
{...props}
language={lang?.toLowerCase()}
style={expand ? {} : { height: 0, overflow: 'hidden' }}
style={expand ? {} : { height: 0, overflow: 'hidden', border: 'none' }}
copyable={false}
showLanguage={false}
/>
Expand Down
69 changes: 69 additions & 0 deletions src/Markdown/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { createStyles } from 'antd-style';
import { memo } from 'react';

import { Highlight, Snippet } from '@ant-design/pro-editor';

const useStyles = createStyles(({ css }) => ({
container: css`
:not(:last-child) {
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
}
`,
highlight: css`
pre {
padding: 12px !important;
}
`,
snippet: css`
display: flex;
`,
}));

const countLines = (str: string): number => {
const regex = /\n/g;
const matches = str.match(regex);
return matches ? matches.length : 1;
};

const Code = memo(({ highlight, snippet, ...properties }: any) => {
const { styles, cx } = useStyles();

if (!properties.children[0]) return;

const { children, className } = properties.children[0].props;

if (!children) return;

const content = Array.isArray(children) ? (children[0] as string) : children;
const lang = className?.replace('language-', '') || 'txt';
if (countLines(content) === 1 && content.length <= 60) {
return (
<Snippet
className={cx(styles.container, styles.snippet)}
data-code-type="highlighter"
language={lang}
symbol={''}
type={'block'}
{...snippet}
>
{content}
</Snippet>
);
}

return (
<Highlight
className={cx(styles.container, styles.highlight)}
language={lang}
type="block"
{...highlight}
>
{content}
</Highlight>
);
});

export { Code };
88 changes: 88 additions & 0 deletions src/Markdown/demos/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export const content = `# This is an H1
## This is an H2
### This is an H3
#### This is an H4
##### This is an H5
The point of reference-style links is not that they’re easier to write. The point is that with reference-style links, your document source is vastly more readable. Compare the above examples: using reference-style links, the paragraph itself is only 81 characters long; with inline-style links, it’s 176 characters; and as raw \`HTML\`, it’s 234 characters. In the raw \`HTML\`, there’s more markup than there is text.
---
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
>
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
> id sem consectetuer libero luctus adipiscing.
---
an example | *an example* | **an example**
---
1. Bird
1. McHale
1. Parish
1. Bird
1. McHale
1. Parish
---
- Red
- Green
- Blue
- Red
- Green
- Blue
---
This is [an example](http://example.com/ "Title") inline link.
<http://example.com/>
| title | title | title |
| --- | --- | --- |
| content | content | content |
\`\`\`bash
$ pnpm install
\`\`\`
\`\`\`javascript
import { renderHook } from '@testing-library/react-hooks';
import { act } from 'react-dom/test-utils';
import { useDropNodeOnCanvas } from './useDropNodeOnCanvas';
\`\`\`
---
以下是一段Markdown格式的LaTeX数学公式:
我是一个行内公式:$E=mc^2$
我是一个独立公式:
$$
\\sum_{i=1}^{n} x_i = x_1 + x_2 + \\ldots + x_n
$$
我是一个带有分式的公式:
$$
\\frac{{n!}}{{k!(n-k)!}} = \\binom{n}{k}
$$
我是一个带有上下标的公式:
$$
x^{2} + y^{2} = r^{2}
$$
我是一个带有积分符号的公式:
$$
\\int_{a}^{b} f(x) \\, dx
$$
`;
15 changes: 15 additions & 0 deletions src/Markdown/demos/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Markdown } from '@ant-design/pro-editor';

import { content } from './data';

export default () => {
return (
<Markdown
highlight={{
containerWrapper: true,
}}
>
{content}
</Markdown>
);
};
26 changes: 26 additions & 0 deletions src/Markdown/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
nav: 组件
group: 基础组件
title: Markdown 文档展示
atomId: Markdown
description: Markdown是一个用于渲染Markdown文本的React组件。它支持各种Markdown语法,如标题、列表、链接、图片、代码块等。它通常用于文档、博客和其他文本密集型应用中。
---

## Default

ProEditor 内置了一个默认的 Markdown 渲染器,使用 React-Markdown,使用我们自己的 Highlight 和 Snippet 进行代码块的渲染

你也可以通过自己传入 components 等 React-Markdown 的 Props 来进行自定义,多余的会透传过去。

<code src="./demos/index.tsx" nopadding></code>

## APIs

| 属性名 | 类型 | 描述 |
| ------------- | --------------- | ------------------------------ |
| children | string | 要渲染的 Markdown 内容。 |
| className | string | Markdown 组件的类名。 |
| onDoubleClick | () => void | 双击事件处理函数。 |
| style | CSSProperties | Markdown 组件的样式。 |
| highlight | Highlight Props | Highlight 的配置,会默认透传。 |
| snippet | snippet Props | Snippet 的配置,会默认透传 |
61 changes: 61 additions & 0 deletions src/Markdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Collapse, Divider, Image, Typography } from 'antd';
import { CSSProperties, memo } from 'react';
import ReactMarkdown from 'react-markdown';
import rehypeKatex from 'rehype-katex';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';

import { HighlightProps, SnippetProps } from '@ant-design/pro-editor';
import { Code } from './CodeBlock';
import { useStyles } from './style';

export interface MarkdownProps {
children: string;
/**
* @description ClassName
*/
className?: string;
fullFeaturedCodeBlock?: boolean;
onDoubleClick?: () => void;
style?: CSSProperties;
// Highlight 的配置,会默认透传
highlight?: HighlightProps;
// Snippet 的配置,会默认透传
snippet?: SnippetProps;
}

const Markdown = memo<MarkdownProps>(
({ children, className, style, onDoubleClick, highlight = {}, snippet = {}, ...rest }) => {
const { styles } = useStyles();
const components: any = {
a: Typography.Link,
details: Collapse,
hr: () => <Divider style={{ marginBottom: '1em', marginTop: 0 }} />,
img: Image,
pre: (props) => {
const { children, ...rest } = props;
return (
<Code highlight={highlight} snippet={snippet} {...rest}>
{children}
</Code>
);
},
};

return (
<Typography className={className} onDoubleClick={onDoubleClick} style={style}>
<ReactMarkdown
className={styles.markdown}
components={components}
rehypePlugins={[rehypeKatex]}
remarkPlugins={[remarkGfm, remarkMath]}
{...rest}
>
{children}
</ReactMarkdown>
</Typography>
);
},
);

export default Markdown;
Loading

0 comments on commit c2a241e

Please sign in to comment.