Skip to content

Commit

Permalink
feat: add nohighlight component.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaywcjlove committed Nov 29, 2023
1 parent f3c67d7 commit 1dc83f5
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 132 deletions.
28 changes: 28 additions & 0 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,34 @@ function App() {
}
```
## Remove Code Highlight
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.
```jsx
import React, { useState } from "react";
import CodeEditor from '@uiw/react-textarea-code-editor/nohighlight';

export default function App() {
const [code, setCode] = useState(
`function add(a, b) {\n return a + b;\n}`
);
return (
<CodeEditor
value={code}
language="js"
placeholder="Please enter JS code."
onChange={(evn) => setCode(evn.target.value)}
padding={15}
style={{
backgroundColor: "#f5f5f5",
fontFamily: 'ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace',
}}
/>
);
}
```
## Support Nextjs
Use examples in nextjs. [#31](https://github.com/uiwjs/react-textarea-code-editor/issues/31#issuecomment-909363339)
Expand Down
15 changes: 15 additions & 0 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@
"funding": "https://jaywcjlove.github.io/#/sponsor",
"main": "cjs/index.js",
"module": "esm/index.js",
"exports": {
"./README.md": "./README.md",
"./package.json": "./package.json",
"./dist.css": "./dist.css",
".": {
"import": "./esm/index.js",
"types": "./cjs/index.d.ts",
"require": "./cjs/index.js"
},
"./nohighlight": {
"import": "./esm/index.nohighlight.js",
"types": "./cjs/index.nohighlight.d.ts",
"require": "./cjs/index.nohighlight.js"
}
},
"repository": {
"type": "git",
"url": "https://github.com/uiwjs/react-textarea-code-editor.git"
Expand Down
130 changes: 130 additions & 0 deletions core/src/Editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { PluggableList } from 'unified';
import { processHtml, htmlEncode } from './utils';
import shortcuts from './shortcuts';
import * as styles from './styles';
import './style/index.less';

export * from './SelectionText';

export interface TextareaCodeEditorProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
prefixCls?: string;
/**
* Support dark-mode/night-mode
*/
['data-color-mode']?: 'dark' | 'light';
/**
* Set what programming language the code belongs to.
*/
language?: string;
/**
* Optional padding for code. Default: `10`.
*/
padding?: number;
/**
* rehypePlugins (Array.<Plugin>, default: `[[rehypePrism, { ignoreMissing: true }]]`)
* 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
*/
rehypePlugins?: PluggableList;
/**
* The minimum height of the editor. Default: `16`.
*/
minHeight?: number;
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void | boolean;
}

export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
const {
prefixCls = 'w-tc-editor',
value: _,
padding = 10,
minHeight = 16,
placeholder,
language,
'data-color-mode': dataColorMode,
className,
style,
rehypePlugins,
onChange,
...other
} = props;

const [value, setValue] = useState(props.value || '');
useEffect(() => setValue(props.value || ''), [props.value]);
const textRef = useRef<HTMLTextAreaElement>(null);
useImperativeHandle<HTMLTextAreaElement, HTMLTextAreaElement>(ref, () => textRef.current!, [textRef]);

const contentStyle = {
paddingTop: padding,
paddingRight: padding,
paddingBottom: padding,
paddingLeft: padding,
};

const htmlStr = useMemo(
() =>
processHtml(
`<pre aria-hidden=true><code ${language && value ? `class="language-${language}"` : ''} >${htmlEncode(
String(value || ''),
)}</code><br /></pre>`,
rehypePlugins,
),
[value, language, rehypePlugins],
);
const preView = useMemo(
() => (
<div
style={{ ...styles.editor, ...contentStyle, minHeight }}
className={`${prefixCls}-preview ${language ? `language-${language}` : ''}`}
dangerouslySetInnerHTML={{
__html: htmlStr,
}}
/>
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[prefixCls, language, htmlStr],
);

const change = (evn: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(evn.target.value);
onChange && onChange(evn);
};

const keyDown = (evn: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (other.readOnly) return;
if (!other.onKeyDown || other.onKeyDown(evn) !== false) {
shortcuts(evn);
}
};

const textareaProps: React.TextareaHTMLAttributes<HTMLTextAreaElement> = {
autoComplete: 'off',
autoCorrect: 'off',
spellCheck: 'false',
autoCapitalize: 'off',
...other,
placeholder,
onKeyDown: keyDown,
style: {
...styles.editor,
...styles.textarea,
...contentStyle,
minHeight,
...(placeholder && !value ? { WebkitTextFillColor: 'inherit' } : {}),
},
onChange: change,
className: `${prefixCls}-text`,
value: value,
};

return (
<div
style={{ ...styles.container, ...style }}
className={`${prefixCls} ${className || ''}`}
data-color-mode={dataColorMode}
>
<textarea {...textareaProps} ref={textRef} />
{preView}
</div>
);
});
6 changes: 6 additions & 0 deletions core/src/index.nohighlight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';
import Editor, { TextareaCodeEditorProps } from './Editor';

export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
return <Editor {...props} ref={ref} />;
});
133 changes: 6 additions & 127 deletions core/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,130 +1,9 @@
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { PluggableList } from 'unified';
import { processHtml, htmlEncode } from './utils';
import shortcuts from './shortcuts';
import * as styles from './styles';
import './style/index.less';

export * from './SelectionText';

export interface TextareaCodeEditorProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
prefixCls?: string;
/**
* Support dark-mode/night-mode
*/
['data-color-mode']?: 'dark' | 'light';
/**
* Set what programming language the code belongs to.
*/
language?: string;
/**
* Optional padding for code. Default: `10`.
*/
padding?: number;
/**
* rehypePlugins (Array.<Plugin>, default: `[[rehypePrism, { ignoreMissing: true }]]`)
* 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
*/
rehypePlugins?: PluggableList;
/**
* The minimum height of the editor. Default: `16`.
*/
minHeight?: number;
onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void | boolean;
}
import React from 'react';
import { Pluggable } from 'unified';
import Editor, { TextareaCodeEditorProps } from './Editor';
import rehypePrism from 'rehype-prism-plus';

export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
const {
prefixCls = 'w-tc-editor',
value: _,
padding = 10,
minHeight = 16,
placeholder,
language,
'data-color-mode': dataColorMode,
className,
style,
rehypePlugins,
onChange,
...other
} = props;

const [value, setValue] = useState(props.value || '');
useEffect(() => setValue(props.value || ''), [props.value]);
const textRef = useRef<HTMLTextAreaElement>(null);
useImperativeHandle<HTMLTextAreaElement, HTMLTextAreaElement>(ref, () => textRef.current!, [textRef]);

const contentStyle = {
paddingTop: padding,
paddingRight: padding,
paddingBottom: padding,
paddingLeft: padding,
};

const htmlStr = useMemo(
() =>
processHtml(
`<pre aria-hidden=true><code ${language && value ? `class="language-${language}"` : ''} >${htmlEncode(
String(value || ''),
)}</code><br /></pre>`,
rehypePlugins,
),
[value, language, rehypePlugins],
);
const preView = useMemo(
() => (
<div
style={{ ...styles.editor, ...contentStyle, minHeight }}
className={`${prefixCls}-preview ${language ? `language-${language}` : ''}`}
dangerouslySetInnerHTML={{
__html: htmlStr,
}}
/>
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[prefixCls, language, htmlStr],
);

const change = (evn: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(evn.target.value);
onChange && onChange(evn);
};

const keyDown = (evn: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (other.readOnly) return;
if (!other.onKeyDown || other.onKeyDown(evn) !== false) {
shortcuts(evn);
}
};

const textareaProps: React.TextareaHTMLAttributes<HTMLTextAreaElement> = {
autoComplete: 'off',
autoCorrect: 'off',
spellCheck: 'false',
autoCapitalize: 'off',
...other,
placeholder,
onKeyDown: keyDown,
style: {
...styles.editor,
...styles.textarea,
...contentStyle,
minHeight,
...(placeholder && !value ? { WebkitTextFillColor: 'inherit' } : {}),
},
onChange: change,
className: `${prefixCls}-text`,
value: value,
};

return (
<div
style={{ ...styles.container, ...style }}
className={`${prefixCls} ${className || ''}`}
data-color-mode={dataColorMode}
>
<textarea {...textareaProps} ref={textRef} />
{preView}
</div>
);
const { rehypePlugins = [[rehypePrism, { ignoreMissing: true }]] as Pluggable[], ...reset } = props;
return <Editor {...reset} rehypePlugins={rehypePlugins} ref={ref} />;
});
6 changes: 1 addition & 5 deletions core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { PluggableList, Pluggable } from 'unified';
import { rehype } from 'rehype';
import rehypePrism from 'rehype-prism-plus';

export const processHtml = (
html: string,
plugins: PluggableList = [[rehypePrism, { ignoreMissing: true }]] as Pluggable[],
) => {
export const processHtml = (html: string, plugins: PluggableList = [] as Pluggable[]) => {
return rehype()
.data('settings', { fragment: true })
.use([...plugins])
Expand Down

0 comments on commit 1dc83f5

Please sign in to comment.