Skip to content

Commit 2836e47

Browse files
committed
feat: add no highlight component.
uiwjs/react-md-editor#586
1 parent ad3bd79 commit 2836e47

File tree

9 files changed

+250
-109
lines changed

9 files changed

+250
-109
lines changed

core/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,67 @@ export default function Demo() {
147147
}
148148
```
149149

150+
## Code Highlight
151+
152+
```jsx mdx:preview
153+
import React from 'react';
154+
import MarkdownPreview from '@uiw/react-markdown-preview';
155+
156+
const source = `
157+
\`\`\`js
158+
function () {
159+
console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
160+
}
161+
\`\`\`
162+
\`\`\`js
163+
function () {
164+
console.log('hello ')
165+
}
166+
\`\`\`
167+
`;
168+
169+
export default function Demo() {
170+
return (
171+
<MarkdownPreview source={source} />
172+
);
173+
}
174+
```
175+
176+
## Remove Code Highlight
177+
178+
The following example can help you _exclude code highlighting code_<!--rehype:style=color: #333;background-color: rgb(196 255 122 / 86%);--> from being included in the bundle. `@uiw/react-markdown-preview/nohighlight`<!--rehype:style=color: #e24444;--> component does not contain the `rehype-prism-plus` code highlighting package, `showLineNumbers` and `highlight line` functions will no longer work. ([#586](https://github.com/uiwjs/react-md-editor/issues/586))
179+
180+
```jsx mdx:preview
181+
import React from 'react';
182+
import MarkdownPreview from '@uiw/react-markdown-preview/nohighlight';
183+
184+
const source = `
185+
\`\`\`js showLineNumbers
186+
function () {
187+
console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
188+
}
189+
\`\`\`
190+
\`\`\`js showLineNumbers {2}
191+
function () {
192+
console.log('hello ')
193+
}
194+
\`\`\`
195+
`;
196+
197+
export default function Demo() {
198+
return (
199+
<MarkdownPreview
200+
source={source}
201+
rehypeRewrite={(node, index, parent) => {
202+
if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
203+
parent.children = parent.children.slice(1)
204+
}
205+
}}
206+
/>
207+
);
208+
}
209+
```
210+
150211
## Ignore
151212

152213
Ignore content display via HTML comments, Shown in GitHub readme, excluded in HTML.

core/nohighlight.d.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
declare module '@uiw/react-markdown-preview/nohighlight' {
2+
import React from 'react';
3+
import { Options } from 'react-markdown';
4+
import { PluggableList } from 'unified';
5+
import { RehypeRewriteOptions } from 'rehype-rewrite';
6+
export interface MarkdownPreviewProps extends Omit<Options, 'children'> {
7+
prefixCls?: string;
8+
className?: string;
9+
source?: string;
10+
disableCopy?: boolean;
11+
style?: React.CSSProperties;
12+
pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
13+
wrapperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
14+
'data-color-mode'?: 'light' | 'dark';
15+
};
16+
/**
17+
* Please use wrapperElement, Will be removed in v5 release.
18+
* @deprecated
19+
*/
20+
warpperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
21+
'data-color-mode'?: 'light' | 'dark';
22+
};
23+
onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
24+
onMouseOver?: (e: React.MouseEvent<HTMLDivElement>) => void;
25+
rehypeRewrite?: RehypeRewriteOptions['rewrite'];
26+
}
27+
export interface MarkdownPreviewRef extends MarkdownPreviewProps {
28+
mdp: React.RefObject<HTMLDivElement>;
29+
}
30+
const _default: React.ForwardRefExoticComponent<MarkdownPreviewProps & React.RefAttributes<MarkdownPreviewRef>>;
31+
export default _default;
32+
}

core/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@
55
"homepage": "https://uiwjs.github.io/react-markdown-preview",
66
"main": "lib/index.js",
77
"module": "esm/index.js",
8+
"exports": {
9+
"./README.md": "./README.md",
10+
"./package.json": "./package.json",
11+
".": {
12+
"import": "./esm/index.js",
13+
"types": "./lib/index.d.ts",
14+
"require": "./lib/index.js"
15+
},
16+
"./nohighlight": {
17+
"import": "./esm/nohighlight.js",
18+
"types": "./lib/nohighlight.d.ts",
19+
"require": "./lib/nohighlight.js"
20+
}
21+
},
822
"scripts": {
923
"css:build": "compile-less -d src -o esm",
1024
"css:watch": "compile-less -d src -o esm --watch",

core/src/index.tsx

Lines changed: 9 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,20 @@
1-
import React, { useImperativeHandle } from 'react';
2-
import ReactMarkdown, { Options } from 'react-markdown';
3-
import { Element } from 'hast';
1+
import React from 'react';
2+
import MarkdownPreview, { type MarkdownPreviewProps, type MarkdownPreviewRef } from './preview';
3+
import rehypePrism from 'rehype-prism-plus';
44
import { PluggableList } from 'unified';
5-
import gfm from 'remark-gfm';
6-
import raw from 'rehype-raw';
7-
import slug from 'rehype-slug';
8-
import headings from 'rehype-autolink-headings';
5+
import rehypeRewrite from 'rehype-rewrite';
96
import rehypeAttrs from 'rehype-attr';
10-
import rehypeIgnore from 'rehype-ignore';
11-
import rehypePrism from 'rehype-prism-plus';
12-
import rehypeRewrite, { getCodeString, RehypeRewriteOptions } from 'rehype-rewrite';
13-
import { octiconLink } from './nodes/octiconLink';
14-
import { copyElement } from './nodes/copy';
15-
import { useCopied } from './plugins/useCopied';
16-
import './styles/markdown.less';
17-
187
import { reservedMeta } from './plugins/reservedMeta';
19-
20-
export interface MarkdownPreviewProps extends Omit<Options, 'children'> {
21-
prefixCls?: string;
22-
className?: string;
23-
source?: string;
24-
disableCopy?: boolean;
25-
style?: React.CSSProperties;
26-
pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
27-
wrapperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
28-
'data-color-mode'?: 'light' | 'dark';
29-
};
30-
/**
31-
* Please use wrapperElement, Will be removed in v5 release.
32-
* @deprecated
33-
*/
34-
warpperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
35-
'data-color-mode'?: 'light' | 'dark';
36-
};
37-
onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
38-
onMouseOver?: (e: React.MouseEvent<HTMLDivElement>) => void;
39-
rehypeRewrite?: RehypeRewriteOptions['rewrite'];
40-
}
41-
42-
export interface MarkdownPreviewRef extends MarkdownPreviewProps {
43-
mdp: React.RefObject<HTMLDivElement>;
44-
}
8+
import { rehypeRewriteHandle, defaultRehypePlugins } from './rehypePlugins';
459

4610
export default React.forwardRef<MarkdownPreviewRef, MarkdownPreviewProps>((props, ref) => {
47-
const {
48-
prefixCls = 'wmde-markdown wmde-markdown-color',
49-
className,
50-
source,
51-
style,
52-
disableCopy = false,
53-
skipHtml = true,
54-
onScroll,
55-
onMouseOver,
56-
pluginsFilter,
57-
rehypeRewrite: rewrite,
58-
wrapperElement = {},
59-
warpperElement = {},
60-
...other
61-
} = props;
62-
const mdp = React.useRef<HTMLDivElement>(null);
63-
useImperativeHandle(ref, () => ({ ...props, mdp }), [mdp, props]);
64-
const cls = `${prefixCls || ''} ${className || ''}`;
65-
useCopied(mdp);
66-
67-
const rehypeRewriteHandle: RehypeRewriteOptions['rewrite'] = (node, index, parent) => {
68-
if (node.type === 'element' && parent && parent.type === 'root' && /h(1|2|3|4|5|6)/.test(node.tagName)) {
69-
const child = node.children && (node.children[0] as Element);
70-
if (child && child.properties && child.properties.ariaHidden === 'true') {
71-
child.properties = { class: 'anchor', ...child.properties };
72-
child.children = [octiconLink];
73-
}
74-
}
75-
if (node.type === 'element' && node.tagName === 'pre' && !disableCopy) {
76-
const code = getCodeString(node.children);
77-
node.children.push(copyElement(code));
78-
}
79-
rewrite && rewrite(node, index, parent);
80-
};
81-
8211
const rehypePlugins: PluggableList = [
8312
reservedMeta,
8413
[rehypePrism, { ignoreMissing: true }],
85-
slug,
86-
headings,
87-
rehypeIgnore,
88-
[rehypeRewrite, { rewrite: rehypeRewriteHandle }],
14+
...defaultRehypePlugins,
15+
[rehypeRewrite, { rewrite: rehypeRewriteHandle(props.disableCopy ?? false, props.rehypeRewrite) }],
8916
[rehypeAttrs, { properties: 'attr' }],
90-
...(other.rehypePlugins || []),
17+
...(props.rehypePlugins || []),
9118
];
92-
const customProps: MarkdownPreviewProps = {
93-
allowElement: (element, index, parent) => {
94-
if (other.allowElement) {
95-
return other.allowElement(element, index, parent);
96-
}
97-
return /^[A-Za-z0-9]+$/.test(element.tagName);
98-
},
99-
};
100-
if (skipHtml) {
101-
rehypePlugins.push(raw);
102-
}
103-
const remarkPlugins = [...(other.remarkPlugins || []), gfm];
104-
const wrapperProps = { ...warpperElement, ...wrapperElement };
105-
return (
106-
<div ref={mdp} onScroll={onScroll} onMouseOver={onMouseOver} {...wrapperProps} className={cls} style={style}>
107-
<ReactMarkdown
108-
{...customProps}
109-
{...other}
110-
skipHtml={skipHtml}
111-
rehypePlugins={pluginsFilter ? pluginsFilter('rehype', rehypePlugins) : rehypePlugins}
112-
remarkPlugins={pluginsFilter ? pluginsFilter('remark', remarkPlugins) : remarkPlugins}
113-
children={source || ''}
114-
/>
115-
</div>
116-
);
19+
return <MarkdownPreview {...props} rehypePlugins={rehypePlugins} ref={ref} />;
11720
});

core/src/nohighlight.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import MarkdownPreview, { type MarkdownPreviewProps, type MarkdownPreviewRef } from './preview';
3+
import { PluggableList } from 'unified';
4+
import rehypeRewrite from 'rehype-rewrite';
5+
import { reservedMeta } from './plugins/reservedMeta';
6+
import rehypeAttrs from 'rehype-attr';
7+
import { rehypeRewriteHandle, defaultRehypePlugins } from './rehypePlugins';
8+
9+
export default React.forwardRef<MarkdownPreviewRef, MarkdownPreviewProps>((props, ref) => {
10+
const rehypePlugins: PluggableList = [
11+
reservedMeta,
12+
...defaultRehypePlugins,
13+
[rehypeRewrite, { rewrite: rehypeRewriteHandle(props.disableCopy ?? false, props.rehypeRewrite) }],
14+
[rehypeAttrs, { properties: 'attr' }],
15+
...(props.rehypePlugins || []),
16+
];
17+
return <MarkdownPreview {...props} rehypePlugins={rehypePlugins} ref={ref} />;
18+
});

core/src/preview.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React, { useImperativeHandle } from 'react';
2+
import ReactMarkdown, { Options } from 'react-markdown';
3+
import { PluggableList } from 'unified';
4+
import gfm from 'remark-gfm';
5+
import raw from 'rehype-raw';
6+
import { type RehypeRewriteOptions } from 'rehype-rewrite';
7+
import { useCopied } from './plugins/useCopied';
8+
import './styles/markdown.less';
9+
10+
export interface MarkdownPreviewProps extends Omit<Options, 'children'> {
11+
prefixCls?: string;
12+
className?: string;
13+
source?: string;
14+
disableCopy?: boolean;
15+
style?: React.CSSProperties;
16+
pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
17+
wrapperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
18+
'data-color-mode'?: 'light' | 'dark';
19+
};
20+
/**
21+
* Please use wrapperElement, Will be removed in v5 release.
22+
* @deprecated
23+
*/
24+
warpperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
25+
'data-color-mode'?: 'light' | 'dark';
26+
};
27+
onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
28+
onMouseOver?: (e: React.MouseEvent<HTMLDivElement>) => void;
29+
rehypeRewrite?: RehypeRewriteOptions['rewrite'];
30+
}
31+
32+
export interface MarkdownPreviewRef extends MarkdownPreviewProps {
33+
mdp: React.RefObject<HTMLDivElement>;
34+
}
35+
36+
export default React.forwardRef<MarkdownPreviewRef, MarkdownPreviewProps>((props, ref) => {
37+
const {
38+
prefixCls = 'wmde-markdown wmde-markdown-color',
39+
className,
40+
source,
41+
style,
42+
disableCopy = false,
43+
skipHtml = true,
44+
onScroll,
45+
onMouseOver,
46+
pluginsFilter,
47+
rehypeRewrite: rewrite,
48+
wrapperElement = {},
49+
warpperElement = {},
50+
...other
51+
} = props;
52+
const mdp = React.useRef<HTMLDivElement>(null);
53+
useImperativeHandle(ref, () => ({ ...props, mdp }), [mdp, props]);
54+
const cls = `${prefixCls || ''} ${className || ''}`;
55+
useCopied(mdp);
56+
57+
const rehypePlugins: PluggableList = [...(other.rehypePlugins || [])];
58+
const customProps: MarkdownPreviewProps = {
59+
allowElement: (element, index, parent) => {
60+
if (other.allowElement) {
61+
return other.allowElement(element, index, parent);
62+
}
63+
return /^[A-Za-z0-9]+$/.test(element.tagName);
64+
},
65+
};
66+
if (skipHtml) {
67+
rehypePlugins.push(raw);
68+
}
69+
const remarkPlugins = [...(other.remarkPlugins || []), gfm];
70+
const wrapperProps = { ...warpperElement, ...wrapperElement };
71+
return (
72+
<div ref={mdp} onScroll={onScroll} onMouseOver={onMouseOver} {...wrapperProps} className={cls} style={style}>
73+
<ReactMarkdown
74+
{...customProps}
75+
{...other}
76+
skipHtml={skipHtml}
77+
rehypePlugins={pluginsFilter ? pluginsFilter('rehype', rehypePlugins) : rehypePlugins}
78+
remarkPlugins={pluginsFilter ? pluginsFilter('remark', remarkPlugins) : remarkPlugins}
79+
children={source || ''}
80+
/>
81+
</div>
82+
);
83+
});

core/src/rehypePlugins.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { PluggableList } from 'unified';
2+
import slug from 'rehype-slug';
3+
import headings from 'rehype-autolink-headings';
4+
import rehypeIgnore from 'rehype-ignore';
5+
import { getCodeString, RehypeRewriteOptions } from 'rehype-rewrite';
6+
import { octiconLink } from './nodes/octiconLink';
7+
import { copyElement } from './nodes/copy';
8+
import { Root, Element, RootContent } from 'hast';
9+
10+
export const rehypeRewriteHandle =
11+
(disableCopy: boolean, rewrite?: RehypeRewriteOptions['rewrite']) =>
12+
(node: Root | RootContent, index: number | null, parent: Root | Element | null) => {
13+
if (node.type === 'element' && parent && parent.type === 'root' && /h(1|2|3|4|5|6)/.test(node.tagName)) {
14+
const child = node.children && (node.children[0] as Element);
15+
if (child && child.properties && child.properties.ariaHidden === 'true') {
16+
child.properties = { class: 'anchor', ...child.properties };
17+
child.children = [octiconLink];
18+
}
19+
}
20+
if (node.type === 'element' && node.tagName === 'pre' && !disableCopy) {
21+
const code = getCodeString(node.children);
22+
node.children.push(copyElement(code));
23+
}
24+
rewrite && rewrite(node, index, parent);
25+
};
26+
27+
export const defaultRehypePlugins: PluggableList = [slug, headings, rehypeIgnore];

website/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"private": true,
55
"scripts": {
66
"build": "kkt build",
7-
"start": "kkt start"
7+
"start": "kkt start",
8+
"map": "source-map-explorer build/static/js/*.js --html build/website-result.html"
89
},
910
"dependencies": {
1011
"@uiw/react-markdown-preview-example": "^1.5.5",
@@ -21,7 +22,8 @@
2122
"markdown-react-code-preview-loader": "^2.1.5",
2223
"prettier": "^2.8.4",
2324
"pretty-quick": "^3.1.3",
24-
"react-test-renderer": "^18.2.0"
25+
"react-test-renderer": "^18.2.0",
26+
"source-map-explorer": "~2.5.2"
2527
},
2628
"eslintConfig": {
2729
"extends": "react-app"

0 commit comments

Comments
 (0)