@uiw/react-md-editor 4.0.10 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +39 -4
  2. package/common.d.ts +17 -0
  3. package/dist/mdeditor.css +105 -110
  4. package/dist/mdeditor.js +7122 -9557
  5. package/dist/mdeditor.min.css +1 -1
  6. package/dist/mdeditor.min.js +1 -1
  7. package/dist/mdeditor.min.js.LICENSE.txt +2 -2
  8. package/esm/Editor.common.d.ts +5 -0
  9. package/esm/Editor.common.js +7 -0
  10. package/esm/Editor.d.ts +4 -11
  11. package/esm/Editor.factory.d.ts +14 -0
  12. package/esm/Editor.factory.js +270 -0
  13. package/esm/Editor.js +5 -261
  14. package/esm/Editor.nohighlight.d.ts +4 -11
  15. package/esm/Editor.nohighlight.js +5 -261
  16. package/esm/components/TextArea/Markdown.common.d.ts +5 -0
  17. package/esm/components/TextArea/Markdown.common.js +59 -0
  18. package/esm/components/TextArea/factory.d.ts +33 -0
  19. package/esm/components/TextArea/factory.js +105 -0
  20. package/esm/components/TextArea/index.common.d.ts +3 -0
  21. package/esm/components/TextArea/index.common.js +6 -0
  22. package/esm/components/TextArea/index.d.ts +3 -27
  23. package/esm/components/TextArea/index.js +5 -100
  24. package/esm/components/TextArea/index.nohighlight.d.ts +3 -27
  25. package/esm/components/TextArea/index.nohighlight.js +2 -93
  26. package/esm/index.common.d.ts +16 -0
  27. package/esm/index.common.js +16 -0
  28. package/esm/index.css +0 -1
  29. package/esm/react-markdown-preview.common.d.ts +4 -0
  30. package/lib/Editor.common.d.ts +5 -0
  31. package/lib/Editor.common.js +15 -0
  32. package/lib/Editor.d.ts +4 -11
  33. package/lib/Editor.factory.d.ts +14 -0
  34. package/lib/Editor.factory.js +333 -0
  35. package/lib/Editor.js +4 -318
  36. package/lib/Editor.nohighlight.d.ts +4 -11
  37. package/lib/Editor.nohighlight.js +4 -318
  38. package/lib/components/TextArea/Markdown.common.d.ts +5 -0
  39. package/lib/components/TextArea/Markdown.common.js +68 -0
  40. package/lib/components/TextArea/factory.d.ts +33 -0
  41. package/lib/components/TextArea/factory.js +109 -0
  42. package/lib/components/TextArea/index.common.d.ts +3 -0
  43. package/lib/components/TextArea/index.common.js +14 -0
  44. package/lib/components/TextArea/index.d.ts +3 -27
  45. package/lib/components/TextArea/index.js +6 -98
  46. package/lib/components/TextArea/index.nohighlight.d.ts +3 -27
  47. package/lib/components/TextArea/index.nohighlight.js +3 -92
  48. package/lib/index.common.d.ts +16 -0
  49. package/lib/index.common.js +123 -0
  50. package/lib/react-markdown-preview.common.d.ts +4 -0
  51. package/markdown-editor.css +0 -1
  52. package/nohighlight.d.ts +2 -0
  53. package/package.json +8 -2
  54. package/src/Editor.common.tsx +7 -0
  55. package/src/Editor.factory.tsx +286 -0
  56. package/src/Editor.nohighlight.tsx +3 -271
  57. package/src/Editor.tsx +3 -271
  58. package/src/components/TextArea/Markdown.common.tsx +49 -0
  59. package/src/components/TextArea/factory.tsx +120 -0
  60. package/src/components/TextArea/index.common.tsx +6 -0
  61. package/src/components/TextArea/index.nohighlight.tsx +3 -108
  62. package/src/components/TextArea/index.tsx +3 -110
  63. package/src/index.common.tsx +19 -0
  64. package/src/react-markdown-preview.common.d.ts +4 -0
package/src/Editor.tsx CHANGED
@@ -1,275 +1,7 @@
1
- import React, { useEffect, useReducer, useMemo, useRef, useImperativeHandle } from 'react';
2
1
  import MarkdownPreview from '@uiw/react-markdown-preview';
3
- import { ToolbarVisibility } from './components/Toolbar/';
4
2
  import TextArea from './components/TextArea/';
5
- import DragBar from './components/DragBar/';
6
- import { getCommands, getExtraCommands, type ICommand, type TextState, TextAreaCommandOrchestrator } from './commands/';
7
- import { reducer, EditorContext, type ContextStore } from './Context';
8
- import type { MDEditorProps } from './Types';
3
+ import { createMDEditor } from './Editor.factory';
9
4
 
10
- function setGroupPopFalse(data: Record<string, boolean> = {}) {
11
- Object.keys(data).forEach((keyname) => {
12
- data[keyname] = false;
13
- });
14
- return data;
15
- }
5
+ export type { RefMDEditor } from './Editor.factory';
16
6
 
17
- export interface RefMDEditor extends ContextStore {}
18
-
19
- const InternalMDEditor = React.forwardRef<RefMDEditor, MDEditorProps>(
20
- (props: MDEditorProps, ref: React.ForwardedRef<RefMDEditor>) => {
21
- const {
22
- prefixCls = 'w-md-editor',
23
- className,
24
- value: propsValue,
25
- commands = getCommands(),
26
- commandsFilter,
27
- direction,
28
- extraCommands = getExtraCommands(),
29
- height = 200,
30
- enableScroll = true,
31
- visibleDragbar = typeof props.visiableDragbar === 'boolean' ? props.visiableDragbar : true,
32
- highlightEnable = true,
33
- preview: previewType = 'live',
34
- fullscreen = false,
35
- overflow = true,
36
- previewOptions = {},
37
- textareaProps,
38
- maxHeight = 1200,
39
- minHeight = 100,
40
- autoFocus,
41
- autoFocusEnd = false,
42
- tabSize = 2,
43
- defaultTabEnable = false,
44
- onChange,
45
- onStatistics,
46
- onHeightChange,
47
- hideToolbar,
48
- toolbarBottom = false,
49
- components,
50
- renderTextarea,
51
- ...other
52
- } = props || {};
53
- const cmds = commands
54
- .map((item) => (commandsFilter ? commandsFilter(item, false) : item))
55
- .filter(Boolean) as ICommand[];
56
- const extraCmds = extraCommands
57
- .map((item) => (commandsFilter ? commandsFilter(item, true) : item))
58
- .filter(Boolean) as ICommand[];
59
- let [state, dispatch] = useReducer(reducer, {
60
- markdown: propsValue,
61
- preview: previewType,
62
- components,
63
- height,
64
- minHeight,
65
- highlightEnable,
66
- tabSize,
67
- defaultTabEnable,
68
- scrollTop: 0,
69
- scrollTopPreview: 0,
70
- commands: cmds,
71
- extraCommands: extraCmds,
72
- fullscreen,
73
- barPopup: {},
74
- });
75
- const container = useRef<HTMLDivElement>(null);
76
- const previewRef = useRef<HTMLDivElement>(null);
77
- const enableScrollRef = useRef(enableScroll);
78
-
79
- useImperativeHandle(ref, () => ({ ...state, container: container.current, dispatch }));
80
- useMemo(() => (enableScrollRef.current = enableScroll), [enableScroll]);
81
- useEffect(() => {
82
- const stateInit: ContextStore = {};
83
- if (container.current) {
84
- stateInit.container = container.current || undefined;
85
- }
86
- stateInit.markdown = propsValue || '';
87
- stateInit.barPopup = {};
88
- if (dispatch) {
89
- dispatch({ ...state, ...stateInit });
90
- }
91
- // eslint-disable-next-line react-hooks/exhaustive-deps
92
- }, []);
93
-
94
- const cls = [
95
- className,
96
- 'wmde-markdown-var',
97
- direction ? `${prefixCls}-${direction}` : null,
98
- prefixCls,
99
- state.preview ? `${prefixCls}-show-${state.preview}` : null,
100
- state.fullscreen ? `${prefixCls}-fullscreen` : null,
101
- ]
102
- .filter(Boolean)
103
- .join(' ')
104
- .trim();
105
-
106
- useMemo(
107
- () => propsValue !== state.markdown && dispatch({ markdown: propsValue || '' }),
108
- [propsValue, state.markdown],
109
- );
110
- // eslint-disable-next-line react-hooks/exhaustive-deps
111
- useMemo(() => previewType !== state.preview && dispatch({ preview: previewType }), [previewType]);
112
- // eslint-disable-next-line react-hooks/exhaustive-deps
113
- useMemo(() => tabSize !== state.tabSize && dispatch({ tabSize }), [tabSize]);
114
- useMemo(
115
- () => highlightEnable !== state.highlightEnable && dispatch({ highlightEnable }),
116
- // eslint-disable-next-line react-hooks/exhaustive-deps
117
- [highlightEnable],
118
- );
119
- // eslint-disable-next-line react-hooks/exhaustive-deps
120
- useMemo(() => autoFocus !== state.autoFocus && dispatch({ autoFocus: autoFocus }), [autoFocus]);
121
- useMemo(() => autoFocusEnd !== state.autoFocusEnd && dispatch({ autoFocusEnd: autoFocusEnd }), [autoFocusEnd]);
122
- useMemo(
123
- () => fullscreen !== state.fullscreen && dispatch({ fullscreen: fullscreen }),
124
- // eslint-disable-next-line react-hooks/exhaustive-deps
125
- [fullscreen],
126
- );
127
- // eslint-disable-next-line react-hooks/exhaustive-deps
128
- useMemo(() => height !== state.height && dispatch({ height: height }), [height]);
129
- useMemo(
130
- () => height !== state.height && onHeightChange && onHeightChange(state.height, height, state),
131
- [height, onHeightChange, state],
132
- );
133
- // eslint-disable-next-line react-hooks/exhaustive-deps
134
- useMemo(() => commands !== state.commands && dispatch({ commands: cmds }), [props.commands]);
135
- // eslint-disable-next-line react-hooks/exhaustive-deps
136
- useMemo(
137
- () => extraCommands !== state.extraCommands && dispatch({ extraCommands: extraCmds }),
138
- [props.extraCommands],
139
- );
140
-
141
- const textareaDomRef = useRef<HTMLDivElement>();
142
- const active = useRef<'text' | 'preview'>('preview');
143
- const initScroll = useRef(false);
144
-
145
- useMemo(() => {
146
- textareaDomRef.current = state.textareaWarp;
147
- if (state.textareaWarp) {
148
- state.textareaWarp.addEventListener('mouseover', () => {
149
- active.current = 'text';
150
- });
151
- state.textareaWarp.addEventListener('mouseleave', () => {
152
- active.current = 'preview';
153
- });
154
- }
155
- }, [state.textareaWarp]);
156
-
157
- const handleScroll = (e: React.UIEvent<HTMLDivElement>, type: 'text' | 'preview') => {
158
- if (!enableScrollRef.current) return;
159
- const textareaDom = textareaDomRef.current;
160
- const previewDom = previewRef.current ? previewRef.current : undefined;
161
- if (!initScroll.current) {
162
- active.current = type;
163
- initScroll.current = true;
164
- }
165
- if (textareaDom && previewDom) {
166
- const scale =
167
- (textareaDom.scrollHeight - textareaDom.offsetHeight) / (previewDom.scrollHeight - previewDom.offsetHeight);
168
- if (e.target === textareaDom && active.current === 'text') {
169
- previewDom.scrollTop = textareaDom.scrollTop / scale;
170
- }
171
- if (e.target === previewDom && active.current === 'preview') {
172
- textareaDom.scrollTop = previewDom.scrollTop * scale;
173
- }
174
- let scrollTop = 0;
175
- if (active.current === 'text') {
176
- scrollTop = textareaDom.scrollTop || 0;
177
- } else if (active.current === 'preview') {
178
- scrollTop = previewDom.scrollTop || 0;
179
- }
180
- dispatch({ scrollTop });
181
- }
182
- };
183
-
184
- const previewClassName = `${prefixCls}-preview ${previewOptions.className || ''}`;
185
- const handlePreviewScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => handleScroll(e, 'preview');
186
- let mdPreview = useMemo(
187
- () => (
188
- <div ref={previewRef} className={previewClassName}>
189
- <MarkdownPreview {...previewOptions} onScroll={handlePreviewScroll} source={state.markdown || ''} />
190
- </div>
191
- ),
192
- [previewClassName, previewOptions, state.markdown],
193
- );
194
- const preview = components?.preview && components?.preview(state.markdown || '', state, dispatch);
195
- if (preview && React.isValidElement(preview)) {
196
- mdPreview = (
197
- <div className={previewClassName} ref={previewRef} onScroll={handlePreviewScroll}>
198
- {preview}
199
- </div>
200
- );
201
- }
202
-
203
- const containerStyle = { ...other.style, height: state.height || '100%' };
204
- const containerClick = () => dispatch({ barPopup: { ...setGroupPopFalse(state.barPopup) } });
205
- const dragBarChange = (newHeight: number) => dispatch({ height: newHeight });
206
-
207
- const changeHandle = (evn: React.ChangeEvent<HTMLTextAreaElement>) => {
208
- onChange && onChange(evn.target.value, evn, state);
209
- if (textareaProps && textareaProps.onChange) {
210
- textareaProps.onChange(evn);
211
- }
212
- if (state.textarea && state.textarea instanceof HTMLTextAreaElement && onStatistics) {
213
- const obj = new TextAreaCommandOrchestrator(state.textarea!);
214
- const objState = (obj.getState() || {}) as TextState;
215
- onStatistics({
216
- ...objState,
217
- lineCount: evn.target.value.split('\n').length,
218
- length: evn.target.value.length,
219
- });
220
- }
221
- };
222
- return (
223
- <EditorContext.Provider value={{ ...state, dispatch }}>
224
- <div ref={container} className={cls} {...other} onClick={containerClick} style={containerStyle}>
225
- <ToolbarVisibility
226
- hideToolbar={hideToolbar}
227
- toolbarBottom={toolbarBottom}
228
- prefixCls={prefixCls}
229
- overflow={overflow}
230
- placement="top"
231
- />
232
- <div className={`${prefixCls}-content`}>
233
- {/(edit|live)/.test(state.preview || '') && (
234
- <TextArea
235
- className={`${prefixCls}-input`}
236
- prefixCls={prefixCls}
237
- autoFocus={autoFocus}
238
- {...textareaProps}
239
- onChange={changeHandle}
240
- renderTextarea={components?.textarea || renderTextarea}
241
- onScroll={(e) => handleScroll(e, 'text')}
242
- />
243
- )}
244
- {/(live|preview)/.test(state.preview || '') && mdPreview}
245
- </div>
246
- {visibleDragbar && !state.fullscreen && (
247
- <DragBar
248
- prefixCls={prefixCls}
249
- height={state.height as number}
250
- maxHeight={maxHeight!}
251
- minHeight={minHeight!}
252
- onChange={dragBarChange}
253
- />
254
- )}
255
- <ToolbarVisibility
256
- hideToolbar={hideToolbar}
257
- toolbarBottom={toolbarBottom}
258
- prefixCls={prefixCls}
259
- overflow={overflow}
260
- placement="bottom"
261
- />
262
- </div>
263
- </EditorContext.Provider>
264
- );
265
- },
266
- );
267
-
268
- type EditorComponent = typeof InternalMDEditor & {
269
- Markdown: typeof MarkdownPreview;
270
- };
271
-
272
- const Editor = InternalMDEditor as EditorComponent;
273
- Editor.Markdown = MarkdownPreview;
274
-
275
- export default Editor;
7
+ export default createMDEditor({ MarkdownPreview, TextArea });
@@ -0,0 +1,49 @@
1
+ import React, { useContext, useEffect } from 'react';
2
+ import { rehype } from 'rehype';
3
+ import rehypePrism from 'rehype-prism-plus/common';
4
+ import { type IProps } from '../../Types';
5
+ import { EditorContext } from '../../Context';
6
+
7
+ function html2Escape(sHtml: string) {
8
+ return sHtml.replace(
9
+ /[<&"]/g,
10
+ (c: string) => (({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;' }) as Record<string, string>)[c],
11
+ );
12
+ }
13
+
14
+ export interface MarkdownProps extends IProps, React.HTMLAttributes<HTMLPreElement> {}
15
+
16
+ export default function Markdown(props: MarkdownProps) {
17
+ const { prefixCls } = props;
18
+ const { markdown = '', highlightEnable, dispatch } = useContext(EditorContext);
19
+ const preRef = React.createRef<HTMLPreElement>();
20
+ useEffect(() => {
21
+ if (preRef.current && dispatch) {
22
+ dispatch({ textareaPre: preRef.current });
23
+ }
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
25
+ }, []);
26
+ if (!markdown) {
27
+ return <pre ref={preRef} className={`${prefixCls}-text-pre wmde-markdown-color`} />;
28
+ }
29
+ let mdStr = `<pre class="language-markdown ${prefixCls}-text-pre wmde-markdown-color"><code class="language-markdown">${html2Escape(
30
+ String.raw`${markdown}`,
31
+ )}\n</code></pre>`;
32
+
33
+ if (highlightEnable) {
34
+ try {
35
+ mdStr = rehype()
36
+ .data('settings', { fragment: true })
37
+ // https://github.com/uiwjs/react-md-editor/issues/593
38
+ // @ts-ignore
39
+ .use(rehypePrism, { ignoreMissing: true })
40
+ .processSync(mdStr)
41
+ .toString();
42
+ } catch (error) {}
43
+ }
44
+
45
+ return React.createElement('div', {
46
+ className: 'wmde-markdown-color',
47
+ dangerouslySetInnerHTML: { __html: mdStr || '' },
48
+ });
49
+ }
@@ -0,0 +1,120 @@
1
+ import React, { useEffect, Fragment, useContext, JSX } from 'react';
2
+ import type * as CSS from 'csstype';
3
+ import { EditorContext, type ContextStore, type ExecuteCommandState } from '../../Context';
4
+ import shortcuts from './shortcuts';
5
+ import Textarea, { type TextAreaProps } from './Textarea';
6
+ import { type IProps } from '../../Types';
7
+ import { TextAreaCommandOrchestrator, type ICommand } from '../../commands/';
8
+ import './index.less';
9
+
10
+ export type RenderTextareaHandle = {
11
+ dispatch: ContextStore['dispatch'];
12
+ onChange?: TextAreaProps['onChange'];
13
+ useContext?: {
14
+ commands: ContextStore['commands'];
15
+ extraCommands: ContextStore['extraCommands'];
16
+ commandOrchestrator?: TextAreaCommandOrchestrator;
17
+ };
18
+ shortcuts?: (
19
+ e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>,
20
+ commands: ICommand[],
21
+ commandOrchestrator?: TextAreaCommandOrchestrator,
22
+ dispatch?: React.Dispatch<ContextStore>,
23
+ state?: ExecuteCommandState,
24
+ ) => void;
25
+ };
26
+
27
+ export interface ITextAreaProps
28
+ extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onScroll'>,
29
+ IProps {
30
+ value?: string;
31
+ onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
32
+ renderTextarea?: (
33
+ props: React.TextareaHTMLAttributes<HTMLTextAreaElement> | React.HTMLAttributes<HTMLDivElement>,
34
+ opts: RenderTextareaHandle,
35
+ ) => JSX.Element;
36
+ }
37
+
38
+ export type TextAreaRef = {
39
+ text?: HTMLTextAreaElement;
40
+ warp?: HTMLDivElement;
41
+ };
42
+
43
+ type MarkdownComponent = React.ComponentType<{ prefixCls?: string }>;
44
+
45
+ export function createTextArea(options?: { Markdown?: MarkdownComponent; useMinHeight?: boolean }) {
46
+ const MarkdownComponent = options?.Markdown;
47
+ const useMinHeight = options?.useMinHeight ?? false;
48
+
49
+ return function TextArea(props: ITextAreaProps) {
50
+ const { prefixCls, className, onScroll, renderTextarea, ...otherProps } = props || {};
51
+ const { markdown, scrollTop, commands, minHeight, highlightEnable, extraCommands, dispatch } =
52
+ useContext(EditorContext);
53
+ const textRef = React.useRef<HTMLTextAreaElement>(null);
54
+ const executeRef = React.useRef<TextAreaCommandOrchestrator>();
55
+ const warp = React.createRef<HTMLDivElement>();
56
+ useEffect(() => {
57
+ const state: ContextStore = {};
58
+ if (warp.current) {
59
+ state.textareaWarp = warp.current || undefined;
60
+ warp.current.scrollTop = scrollTop || 0;
61
+ }
62
+ if (dispatch) {
63
+ dispatch({ ...state });
64
+ }
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, []);
67
+
68
+ useEffect(() => {
69
+ if (textRef.current && dispatch) {
70
+ const commandOrchestrator = new TextAreaCommandOrchestrator(textRef.current);
71
+ executeRef.current = commandOrchestrator;
72
+ dispatch({ textarea: textRef.current, commandOrchestrator });
73
+ }
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ }, []);
76
+
77
+ const textStyle: CSS.Properties =
78
+ MarkdownComponent && highlightEnable ? {} : { WebkitTextFillColor: 'initial', overflow: 'auto' };
79
+
80
+ return (
81
+ <div ref={warp} className={`${prefixCls}-area ${className || ''}`} onScroll={onScroll}>
82
+ <div className={`${prefixCls}-text`} style={useMinHeight ? { minHeight } : undefined}>
83
+ {renderTextarea ? (
84
+ React.cloneElement(
85
+ renderTextarea(
86
+ {
87
+ ...otherProps,
88
+ value: markdown,
89
+ autoComplete: 'off',
90
+ autoCorrect: 'off',
91
+ spellCheck: 'false',
92
+ autoCapitalize: 'off',
93
+ className: `${prefixCls}-text-input`,
94
+ style: {
95
+ WebkitTextFillColor: 'inherit',
96
+ overflow: 'auto',
97
+ },
98
+ },
99
+ {
100
+ dispatch,
101
+ onChange: otherProps.onChange,
102
+ shortcuts,
103
+ useContext: { commands, extraCommands, commandOrchestrator: executeRef.current },
104
+ },
105
+ ),
106
+ {
107
+ ref: textRef,
108
+ },
109
+ )
110
+ ) : (
111
+ <Fragment>
112
+ {MarkdownComponent && highlightEnable && <MarkdownComponent prefixCls={prefixCls} />}
113
+ <Textarea prefixCls={prefixCls} {...otherProps} style={textStyle} />
114
+ </Fragment>
115
+ )}
116
+ </div>
117
+ </div>
118
+ );
119
+ };
120
+ }
@@ -0,0 +1,6 @@
1
+ import Markdown from './Markdown.common';
2
+ import { createTextArea } from './factory';
3
+
4
+ export type { ITextAreaProps, RenderTextareaHandle, TextAreaRef } from './factory';
5
+
6
+ export default createTextArea({ Markdown, useMinHeight: true });
@@ -1,110 +1,5 @@
1
- import React, { useEffect, Fragment, useContext, JSX } from 'react';
2
- import type * as CSS from 'csstype';
3
- import { EditorContext, type ContextStore, ExecuteCommandState } from '../../Context';
4
- import shortcuts from './shortcuts';
5
- import Textarea, { TextAreaProps } from './Textarea';
6
- import { type IProps } from '../../Types';
7
- import { TextAreaCommandOrchestrator, type ICommand } from '../../commands/';
8
- import './index.less';
1
+ import { createTextArea } from './factory';
9
2
 
10
- type RenderTextareaHandle = {
11
- dispatch: ContextStore['dispatch'];
12
- onChange?: TextAreaProps['onChange'];
13
- useContext?: {
14
- commands: ContextStore['commands'];
15
- extraCommands: ContextStore['extraCommands'];
16
- commandOrchestrator?: TextAreaCommandOrchestrator;
17
- };
18
- shortcuts?: (
19
- e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>,
20
- commands: ICommand[],
21
- commandOrchestrator?: TextAreaCommandOrchestrator,
22
- dispatch?: React.Dispatch<ContextStore>,
23
- state?: ExecuteCommandState,
24
- ) => void;
25
- };
3
+ export type { ITextAreaProps, RenderTextareaHandle, TextAreaRef } from './factory';
26
4
 
27
- export interface ITextAreaProps
28
- extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onScroll'>,
29
- IProps {
30
- value?: string;
31
- onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
32
- renderTextarea?: (
33
- props: React.TextareaHTMLAttributes<HTMLTextAreaElement> | React.HTMLAttributes<HTMLDivElement>,
34
- opts: RenderTextareaHandle,
35
- ) => JSX.Element;
36
- }
37
-
38
- export type TextAreaRef = {
39
- text?: HTMLTextAreaElement;
40
- warp?: HTMLDivElement;
41
- };
42
-
43
- export default function TextArea(props: ITextAreaProps) {
44
- const { prefixCls, className, onScroll, renderTextarea, ...otherProps } = props || {};
45
- const { markdown, scrollTop, commands, extraCommands, dispatch } = useContext(EditorContext);
46
- const textRef = React.useRef<HTMLTextAreaElement>(null);
47
- const executeRef = React.useRef<TextAreaCommandOrchestrator>();
48
- const warp = React.createRef<HTMLDivElement>();
49
- useEffect(() => {
50
- const state: ContextStore = {};
51
- if (warp.current) {
52
- state.textareaWarp = warp.current || undefined;
53
- warp.current.scrollTop = scrollTop || 0;
54
- }
55
- if (dispatch) {
56
- dispatch({ ...state });
57
- }
58
- // eslint-disable-next-line react-hooks/exhaustive-deps
59
- }, []);
60
-
61
- useEffect(() => {
62
- if (textRef.current && dispatch) {
63
- const commandOrchestrator = new TextAreaCommandOrchestrator(textRef.current);
64
- executeRef.current = commandOrchestrator;
65
- dispatch({ textarea: textRef.current, commandOrchestrator });
66
- }
67
- // eslint-disable-next-line react-hooks/exhaustive-deps
68
- }, []);
69
-
70
- const textStyle: CSS.Properties = { WebkitTextFillColor: 'initial', overflow: 'auto' };
71
-
72
- return (
73
- <div ref={warp} className={`${prefixCls}-area ${className || ''}`} onScroll={onScroll}>
74
- <div className={`${prefixCls}-text`}>
75
- {renderTextarea ? (
76
- React.cloneElement(
77
- renderTextarea(
78
- {
79
- ...otherProps,
80
- value: markdown,
81
- autoComplete: 'off',
82
- autoCorrect: 'off',
83
- spellCheck: 'false',
84
- autoCapitalize: 'off',
85
- className: `${prefixCls}-text-input`,
86
- style: {
87
- WebkitTextFillColor: 'inherit',
88
- overflow: 'auto',
89
- },
90
- },
91
- {
92
- dispatch,
93
- onChange: otherProps.onChange,
94
- shortcuts,
95
- useContext: { commands, extraCommands, commandOrchestrator: executeRef.current },
96
- },
97
- ),
98
- {
99
- ref: textRef,
100
- },
101
- )
102
- ) : (
103
- <Fragment>
104
- <Textarea prefixCls={prefixCls} {...otherProps} style={textStyle} />
105
- </Fragment>
106
- )}
107
- </div>
108
- </div>
109
- );
110
- }
5
+ export default createTextArea();
@@ -1,113 +1,6 @@
1
- import React, { useEffect, Fragment, useContext, JSX } from 'react';
2
- import type * as CSS from 'csstype';
3
- import { EditorContext, type ContextStore, type ExecuteCommandState } from '../../Context';
4
- import shortcuts from './shortcuts';
5
1
  import Markdown from './Markdown';
6
- import Textarea, { type TextAreaProps } from './Textarea';
7
- import { type IProps } from '../../Types';
8
- import { TextAreaCommandOrchestrator, type ICommand } from '../../commands/';
9
- import './index.less';
2
+ import { createTextArea } from './factory';
10
3
 
11
- type RenderTextareaHandle = {
12
- dispatch: ContextStore['dispatch'];
13
- onChange?: TextAreaProps['onChange'];
14
- useContext?: {
15
- commands: ContextStore['commands'];
16
- extraCommands: ContextStore['extraCommands'];
17
- commandOrchestrator?: TextAreaCommandOrchestrator;
18
- };
19
- shortcuts?: (
20
- e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>,
21
- commands: ICommand[],
22
- commandOrchestrator?: TextAreaCommandOrchestrator,
23
- dispatch?: React.Dispatch<ContextStore>,
24
- state?: ExecuteCommandState,
25
- ) => void;
26
- };
4
+ export type { ITextAreaProps, RenderTextareaHandle, TextAreaRef } from './factory';
27
5
 
28
- export interface ITextAreaProps
29
- extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onScroll'>,
30
- IProps {
31
- value?: string;
32
- onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
33
- renderTextarea?: (
34
- props: React.TextareaHTMLAttributes<HTMLTextAreaElement> | React.HTMLAttributes<HTMLDivElement>,
35
- opts: RenderTextareaHandle,
36
- ) => JSX.Element;
37
- }
38
-
39
- export type TextAreaRef = {
40
- text?: HTMLTextAreaElement;
41
- warp?: HTMLDivElement;
42
- };
43
-
44
- export default function TextArea(props: ITextAreaProps) {
45
- const { prefixCls, className, onScroll, renderTextarea, ...otherProps } = props || {};
46
- const { markdown, scrollTop, commands, minHeight, highlightEnable, extraCommands, dispatch } =
47
- useContext(EditorContext);
48
- const textRef = React.useRef<HTMLTextAreaElement>(null);
49
- const executeRef = React.useRef<TextAreaCommandOrchestrator>();
50
- const warp = React.createRef<HTMLDivElement>();
51
- useEffect(() => {
52
- const state: ContextStore = {};
53
- if (warp.current) {
54
- state.textareaWarp = warp.current || undefined;
55
- warp.current.scrollTop = scrollTop || 0;
56
- }
57
- if (dispatch) {
58
- dispatch({ ...state });
59
- }
60
- // eslint-disable-next-line react-hooks/exhaustive-deps
61
- }, []);
62
-
63
- useEffect(() => {
64
- if (textRef.current && dispatch) {
65
- const commandOrchestrator = new TextAreaCommandOrchestrator(textRef.current);
66
- executeRef.current = commandOrchestrator;
67
- dispatch({ textarea: textRef.current, commandOrchestrator });
68
- }
69
- // eslint-disable-next-line react-hooks/exhaustive-deps
70
- }, []);
71
-
72
- const textStyle: CSS.Properties = highlightEnable ? {} : { WebkitTextFillColor: 'initial', overflow: 'auto' };
73
-
74
- return (
75
- <div ref={warp} className={`${prefixCls}-area ${className || ''}`} onScroll={onScroll}>
76
- <div className={`${prefixCls}-text`} style={{ minHeight }}>
77
- {renderTextarea ? (
78
- React.cloneElement(
79
- renderTextarea(
80
- {
81
- ...otherProps,
82
- value: markdown,
83
- autoComplete: 'off',
84
- autoCorrect: 'off',
85
- spellCheck: 'false',
86
- autoCapitalize: 'off',
87
- className: `${prefixCls}-text-input`,
88
- style: {
89
- WebkitTextFillColor: 'inherit',
90
- overflow: 'auto',
91
- },
92
- },
93
- {
94
- dispatch,
95
- onChange: otherProps.onChange,
96
- shortcuts,
97
- useContext: { commands, extraCommands, commandOrchestrator: executeRef.current },
98
- },
99
- ),
100
- {
101
- ref: textRef,
102
- },
103
- )
104
- ) : (
105
- <Fragment>
106
- {highlightEnable && <Markdown prefixCls={prefixCls} />}
107
- <Textarea prefixCls={prefixCls} {...otherProps} style={textStyle} />
108
- </Fragment>
109
- )}
110
- </div>
111
- </div>
112
- );
113
- }
6
+ export default createTextArea({ Markdown, useMinHeight: true });