Skip to content

Commit 1dc83f5

Browse files
committed
feat: add nohighlight component.
1 parent f3c67d7 commit 1dc83f5

File tree

6 files changed

+186
-132
lines changed

6 files changed

+186
-132
lines changed

core/README.md

+28
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,34 @@ function App() {
114114
}
115115
```
116116
117+
## Remove Code Highlight
118+
119+
The following example can help you exclude code highlighting code from being included in the bundle. `@uiw/react-textarea-code-editor/nohighlight` component does not contain the ~~`rehype-prism-plus`~~ code highlighting package.
120+
121+
```jsx
122+
import React, { useState } from "react";
123+
import CodeEditor from '@uiw/react-textarea-code-editor/nohighlight';
124+
125+
export default function App() {
126+
const [code, setCode] = useState(
127+
`function add(a, b) {\n return a + b;\n}`
128+
);
129+
return (
130+
<CodeEditor
131+
value={code}
132+
language="js"
133+
placeholder="Please enter JS code."
134+
onChange={(evn) => setCode(evn.target.value)}
135+
padding={15}
136+
style={{
137+
backgroundColor: "#f5f5f5",
138+
fontFamily: 'ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace',
139+
}}
140+
/>
141+
);
142+
}
143+
```
144+
117145
## Support Nextjs
118146
119147
Use examples in nextjs. [#31](https://github.com/uiwjs/react-textarea-code-editor/issues/31#issuecomment-909363339)

core/package.json

+15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@
66
"funding": "https://jaywcjlove.github.io/#/sponsor",
77
"main": "cjs/index.js",
88
"module": "esm/index.js",
9+
"exports": {
10+
"./README.md": "./README.md",
11+
"./package.json": "./package.json",
12+
"./dist.css": "./dist.css",
13+
".": {
14+
"import": "./esm/index.js",
15+
"types": "./cjs/index.d.ts",
16+
"require": "./cjs/index.js"
17+
},
18+
"./nohighlight": {
19+
"import": "./esm/index.nohighlight.js",
20+
"types": "./cjs/index.nohighlight.d.ts",
21+
"require": "./cjs/index.nohighlight.js"
22+
}
23+
},
924
"repository": {
1025
"type": "git",
1126
"url": "https://github.com/uiwjs/react-textarea-code-editor.git"

core/src/Editor.tsx

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2+
import { PluggableList } from 'unified';
3+
import { processHtml, htmlEncode } from './utils';
4+
import shortcuts from './shortcuts';
5+
import * as styles from './styles';
6+
import './style/index.less';
7+
8+
export * from './SelectionText';
9+
10+
export interface TextareaCodeEditorProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
11+
prefixCls?: string;
12+
/**
13+
* Support dark-mode/night-mode
14+
*/
15+
['data-color-mode']?: 'dark' | 'light';
16+
/**
17+
* Set what programming language the code belongs to.
18+
*/
19+
language?: string;
20+
/**
21+
* Optional padding for code. Default: `10`.
22+
*/
23+
padding?: number;
24+
/**
25+
* rehypePlugins (Array.<Plugin>, default: `[[rehypePrism, { ignoreMissing: true }]]`)
26+
* List of [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins) to use. See the next section for examples on how to pass options
27+
*/
28+
rehypePlugins?: PluggableList;
29+
/**
30+
* The minimum height of the editor. Default: `16`.
31+
*/
32+
minHeight?: number;
33+
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void | boolean;
34+
}
35+
36+
export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
37+
const {
38+
prefixCls = 'w-tc-editor',
39+
value: _,
40+
padding = 10,
41+
minHeight = 16,
42+
placeholder,
43+
language,
44+
'data-color-mode': dataColorMode,
45+
className,
46+
style,
47+
rehypePlugins,
48+
onChange,
49+
...other
50+
} = props;
51+
52+
const [value, setValue] = useState(props.value || '');
53+
useEffect(() => setValue(props.value || ''), [props.value]);
54+
const textRef = useRef<HTMLTextAreaElement>(null);
55+
useImperativeHandle<HTMLTextAreaElement, HTMLTextAreaElement>(ref, () => textRef.current!, [textRef]);
56+
57+
const contentStyle = {
58+
paddingTop: padding,
59+
paddingRight: padding,
60+
paddingBottom: padding,
61+
paddingLeft: padding,
62+
};
63+
64+
const htmlStr = useMemo(
65+
() =>
66+
processHtml(
67+
`<pre aria-hidden=true><code ${language && value ? `class="language-${language}"` : ''} >${htmlEncode(
68+
String(value || ''),
69+
)}</code><br /></pre>`,
70+
rehypePlugins,
71+
),
72+
[value, language, rehypePlugins],
73+
);
74+
const preView = useMemo(
75+
() => (
76+
<div
77+
style={{ ...styles.editor, ...contentStyle, minHeight }}
78+
className={`${prefixCls}-preview ${language ? `language-${language}` : ''}`}
79+
dangerouslySetInnerHTML={{
80+
__html: htmlStr,
81+
}}
82+
/>
83+
),
84+
// eslint-disable-next-line react-hooks/exhaustive-deps
85+
[prefixCls, language, htmlStr],
86+
);
87+
88+
const change = (evn: React.ChangeEvent<HTMLTextAreaElement>) => {
89+
setValue(evn.target.value);
90+
onChange && onChange(evn);
91+
};
92+
93+
const keyDown = (evn: React.KeyboardEvent<HTMLTextAreaElement>) => {
94+
if (other.readOnly) return;
95+
if (!other.onKeyDown || other.onKeyDown(evn) !== false) {
96+
shortcuts(evn);
97+
}
98+
};
99+
100+
const textareaProps: React.TextareaHTMLAttributes<HTMLTextAreaElement> = {
101+
autoComplete: 'off',
102+
autoCorrect: 'off',
103+
spellCheck: 'false',
104+
autoCapitalize: 'off',
105+
...other,
106+
placeholder,
107+
onKeyDown: keyDown,
108+
style: {
109+
...styles.editor,
110+
...styles.textarea,
111+
...contentStyle,
112+
minHeight,
113+
...(placeholder && !value ? { WebkitTextFillColor: 'inherit' } : {}),
114+
},
115+
onChange: change,
116+
className: `${prefixCls}-text`,
117+
value: value,
118+
};
119+
120+
return (
121+
<div
122+
style={{ ...styles.container, ...style }}
123+
className={`${prefixCls} ${className || ''}`}
124+
data-color-mode={dataColorMode}
125+
>
126+
<textarea {...textareaProps} ref={textRef} />
127+
{preView}
128+
</div>
129+
);
130+
});

core/src/index.nohighlight.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react';
2+
import Editor, { TextareaCodeEditorProps } from './Editor';
3+
4+
export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
5+
return <Editor {...props} ref={ref} />;
6+
});

core/src/index.tsx

+6-127
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,9 @@
1-
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2-
import { PluggableList } from 'unified';
3-
import { processHtml, htmlEncode } from './utils';
4-
import shortcuts from './shortcuts';
5-
import * as styles from './styles';
6-
import './style/index.less';
7-
8-
export * from './SelectionText';
9-
10-
export interface TextareaCodeEditorProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
11-
prefixCls?: string;
12-
/**
13-
* Support dark-mode/night-mode
14-
*/
15-
['data-color-mode']?: 'dark' | 'light';
16-
/**
17-
* Set what programming language the code belongs to.
18-
*/
19-
language?: string;
20-
/**
21-
* Optional padding for code. Default: `10`.
22-
*/
23-
padding?: number;
24-
/**
25-
* rehypePlugins (Array.<Plugin>, default: `[[rehypePrism, { ignoreMissing: true }]]`)
26-
* List of [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins) to use. See the next section for examples on how to pass options
27-
*/
28-
rehypePlugins?: PluggableList;
29-
/**
30-
* The minimum height of the editor. Default: `16`.
31-
*/
32-
minHeight?: number;
33-
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void | boolean;
34-
}
1+
import React from 'react';
2+
import { Pluggable } from 'unified';
3+
import Editor, { TextareaCodeEditorProps } from './Editor';
4+
import rehypePrism from 'rehype-prism-plus';
355

366
export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
37-
const {
38-
prefixCls = 'w-tc-editor',
39-
value: _,
40-
padding = 10,
41-
minHeight = 16,
42-
placeholder,
43-
language,
44-
'data-color-mode': dataColorMode,
45-
className,
46-
style,
47-
rehypePlugins,
48-
onChange,
49-
...other
50-
} = props;
51-
52-
const [value, setValue] = useState(props.value || '');
53-
useEffect(() => setValue(props.value || ''), [props.value]);
54-
const textRef = useRef<HTMLTextAreaElement>(null);
55-
useImperativeHandle<HTMLTextAreaElement, HTMLTextAreaElement>(ref, () => textRef.current!, [textRef]);
56-
57-
const contentStyle = {
58-
paddingTop: padding,
59-
paddingRight: padding,
60-
paddingBottom: padding,
61-
paddingLeft: padding,
62-
};
63-
64-
const htmlStr = useMemo(
65-
() =>
66-
processHtml(
67-
`<pre aria-hidden=true><code ${language && value ? `class="language-${language}"` : ''} >${htmlEncode(
68-
String(value || ''),
69-
)}</code><br /></pre>`,
70-
rehypePlugins,
71-
),
72-
[value, language, rehypePlugins],
73-
);
74-
const preView = useMemo(
75-
() => (
76-
<div
77-
style={{ ...styles.editor, ...contentStyle, minHeight }}
78-
className={`${prefixCls}-preview ${language ? `language-${language}` : ''}`}
79-
dangerouslySetInnerHTML={{
80-
__html: htmlStr,
81-
}}
82-
/>
83-
),
84-
// eslint-disable-next-line react-hooks/exhaustive-deps
85-
[prefixCls, language, htmlStr],
86-
);
87-
88-
const change = (evn: React.ChangeEvent<HTMLTextAreaElement>) => {
89-
setValue(evn.target.value);
90-
onChange && onChange(evn);
91-
};
92-
93-
const keyDown = (evn: React.KeyboardEvent<HTMLTextAreaElement>) => {
94-
if (other.readOnly) return;
95-
if (!other.onKeyDown || other.onKeyDown(evn) !== false) {
96-
shortcuts(evn);
97-
}
98-
};
99-
100-
const textareaProps: React.TextareaHTMLAttributes<HTMLTextAreaElement> = {
101-
autoComplete: 'off',
102-
autoCorrect: 'off',
103-
spellCheck: 'false',
104-
autoCapitalize: 'off',
105-
...other,
106-
placeholder,
107-
onKeyDown: keyDown,
108-
style: {
109-
...styles.editor,
110-
...styles.textarea,
111-
...contentStyle,
112-
minHeight,
113-
...(placeholder && !value ? { WebkitTextFillColor: 'inherit' } : {}),
114-
},
115-
onChange: change,
116-
className: `${prefixCls}-text`,
117-
value: value,
118-
};
119-
120-
return (
121-
<div
122-
style={{ ...styles.container, ...style }}
123-
className={`${prefixCls} ${className || ''}`}
124-
data-color-mode={dataColorMode}
125-
>
126-
<textarea {...textareaProps} ref={textRef} />
127-
{preView}
128-
</div>
129-
);
7+
const { rehypePlugins = [[rehypePrism, { ignoreMissing: true }]] as Pluggable[], ...reset } = props;
8+
return <Editor {...reset} rehypePlugins={rehypePlugins} ref={ref} />;
1309
});

core/src/utils.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { PluggableList, Pluggable } from 'unified';
22
import { rehype } from 'rehype';
3-
import rehypePrism from 'rehype-prism-plus';
43

5-
export const processHtml = (
6-
html: string,
7-
plugins: PluggableList = [[rehypePrism, { ignoreMissing: true }]] as Pluggable[],
8-
) => {
4+
export const processHtml = (html: string, plugins: PluggableList = [] as Pluggable[]) => {
95
return rehype()
106
.data('settings', { fragment: true })
117
.use([...plugins])

0 commit comments

Comments
 (0)