@uiw/react-md-editor
Version:
A markdown editor with preview, implemented with React.js and TypeScript.
263 lines • 10.2 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose";
var _excluded = ["prefixCls", "className", "value", "commands", "commandsFilter", "direction", "extraCommands", "height", "enableScroll", "visibleDragbar", "highlightEnable", "preview", "fullscreen", "overflow", "previewOptions", "textareaProps", "maxHeight", "minHeight", "autoFocus", "autoFocusEnd", "tabSize", "defaultTabEnable", "onChange", "onStatistics", "onHeightChange", "hideToolbar", "toolbarBottom", "components", "renderTextarea"];
import React, { useEffect, useReducer, useMemo, useRef, useImperativeHandle } from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';
import { ToolbarVisibility } from "./components/Toolbar/index.js";
import TextArea from "./components/TextArea/index.js";
import DragBar from "./components/DragBar/index.js";
import { getCommands, getExtraCommands, TextAreaCommandOrchestrator } from "./commands/index.js";
import { reducer, EditorContext } from "./Context.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function setGroupPopFalse(data) {
if (data === void 0) {
data = {};
}
Object.keys(data).forEach(keyname => {
data[keyname] = false;
});
return data;
}
var InternalMDEditor = /*#__PURE__*/React.forwardRef((props, ref) => {
var _ref = props || {},
{
prefixCls = 'w-md-editor',
className,
value: propsValue,
commands = getCommands(),
commandsFilter,
direction,
extraCommands = getExtraCommands(),
height = 200,
enableScroll = true,
visibleDragbar = typeof props.visiableDragbar === 'boolean' ? props.visiableDragbar : true,
highlightEnable = true,
preview: previewType = 'live',
fullscreen = false,
overflow = true,
previewOptions = {},
textareaProps,
maxHeight = 1200,
minHeight = 100,
autoFocus,
autoFocusEnd = false,
tabSize = 2,
defaultTabEnable = false,
onChange,
onStatistics,
onHeightChange,
hideToolbar,
toolbarBottom = false,
components,
renderTextarea
} = _ref,
other = _objectWithoutPropertiesLoose(_ref, _excluded);
var cmds = commands.map(item => commandsFilter ? commandsFilter(item, false) : item).filter(Boolean);
var extraCmds = extraCommands.map(item => commandsFilter ? commandsFilter(item, true) : item).filter(Boolean);
var [state, dispatch] = useReducer(reducer, {
markdown: propsValue,
preview: previewType,
components,
height,
minHeight,
highlightEnable,
tabSize,
defaultTabEnable,
scrollTop: 0,
scrollTopPreview: 0,
commands: cmds,
extraCommands: extraCmds,
fullscreen,
barPopup: {}
});
var container = useRef(null);
var previewRef = useRef(null);
var enableScrollRef = useRef(enableScroll);
useImperativeHandle(ref, () => _extends({}, state, {
container: container.current,
dispatch
}));
useMemo(() => enableScrollRef.current = enableScroll, [enableScroll]);
useEffect(() => {
var stateInit = {};
if (container.current) {
stateInit.container = container.current || undefined;
}
stateInit.markdown = propsValue || '';
stateInit.barPopup = {};
if (dispatch) {
dispatch(_extends({}, state, stateInit));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
var cls = [className, 'wmde-markdown-var', direction ? prefixCls + "-" + direction : null, prefixCls, state.preview ? prefixCls + "-show-" + state.preview : null, state.fullscreen ? prefixCls + "-fullscreen" : null].filter(Boolean).join(' ').trim();
useMemo(() => propsValue !== state.markdown && dispatch({
markdown: propsValue || ''
}), [propsValue, state.markdown]);
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => previewType !== state.preview && dispatch({
preview: previewType
}), [previewType]);
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => tabSize !== state.tabSize && dispatch({
tabSize
}), [tabSize]);
useMemo(() => highlightEnable !== state.highlightEnable && dispatch({
highlightEnable
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[highlightEnable]);
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => autoFocus !== state.autoFocus && dispatch({
autoFocus: autoFocus
}), [autoFocus]);
useMemo(() => autoFocusEnd !== state.autoFocusEnd && dispatch({
autoFocusEnd: autoFocusEnd
}), [autoFocusEnd]);
useMemo(() => fullscreen !== state.fullscreen && dispatch({
fullscreen: fullscreen
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[fullscreen]);
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => height !== state.height && dispatch({
height: height
}), [height]);
useMemo(() => height !== state.height && onHeightChange && onHeightChange(state.height, height, state), [height, onHeightChange, state]);
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => commands !== state.commands && dispatch({
commands: cmds
}), [props.commands]);
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => extraCommands !== state.extraCommands && dispatch({
extraCommands: extraCmds
}), [props.extraCommands]);
var textareaDomRef = useRef();
var active = useRef('preview');
var initScroll = useRef(false);
useMemo(() => {
textareaDomRef.current = state.textareaWarp;
if (state.textareaWarp) {
state.textareaWarp.addEventListener('mouseover', () => {
active.current = 'text';
});
state.textareaWarp.addEventListener('mouseleave', () => {
active.current = 'preview';
});
}
}, [state.textareaWarp]);
var handleScroll = (e, type) => {
if (!enableScrollRef.current) return;
var textareaDom = textareaDomRef.current;
var previewDom = previewRef.current ? previewRef.current : undefined;
if (!initScroll.current) {
active.current = type;
initScroll.current = true;
}
if (textareaDom && previewDom) {
var scale = (textareaDom.scrollHeight - textareaDom.offsetHeight) / (previewDom.scrollHeight - previewDom.offsetHeight);
if (e.target === textareaDom && active.current === 'text') {
previewDom.scrollTop = textareaDom.scrollTop / scale;
}
if (e.target === previewDom && active.current === 'preview') {
textareaDom.scrollTop = previewDom.scrollTop * scale;
}
var scrollTop = 0;
if (active.current === 'text') {
scrollTop = textareaDom.scrollTop || 0;
} else if (active.current === 'preview') {
scrollTop = previewDom.scrollTop || 0;
}
dispatch({
scrollTop
});
}
};
var previewClassName = prefixCls + "-preview " + (previewOptions.className || '');
var handlePreviewScroll = e => handleScroll(e, 'preview');
var mdPreview = useMemo(() => /*#__PURE__*/_jsx("div", {
ref: previewRef,
className: previewClassName,
children: /*#__PURE__*/_jsx(MarkdownPreview, _extends({}, previewOptions, {
onScroll: handlePreviewScroll,
source: state.markdown || ''
}))
}), [previewClassName, previewOptions, state.markdown]);
var preview = (components == null ? void 0 : components.preview) && (components == null ? void 0 : components.preview(state.markdown || '', state, dispatch));
if (preview && /*#__PURE__*/React.isValidElement(preview)) {
mdPreview = /*#__PURE__*/_jsx("div", {
className: previewClassName,
ref: previewRef,
onScroll: handlePreviewScroll,
children: preview
});
}
var containerStyle = _extends({}, other.style, {
height: state.height || '100%'
});
var containerClick = () => dispatch({
barPopup: _extends({}, setGroupPopFalse(state.barPopup))
});
var dragBarChange = newHeight => dispatch({
height: newHeight
});
var changeHandle = evn => {
onChange && onChange(evn.target.value, evn, state);
if (textareaProps && textareaProps.onChange) {
textareaProps.onChange(evn);
}
if (state.textarea && state.textarea instanceof HTMLTextAreaElement && onStatistics) {
var obj = new TextAreaCommandOrchestrator(state.textarea);
var objState = obj.getState() || {};
onStatistics(_extends({}, objState, {
lineCount: evn.target.value.split('\n').length,
length: evn.target.value.length
}));
}
};
return /*#__PURE__*/_jsx(EditorContext.Provider, {
value: _extends({}, state, {
dispatch
}),
children: /*#__PURE__*/_jsxs("div", _extends({
ref: container,
className: cls
}, other, {
onClick: containerClick,
style: containerStyle,
children: [/*#__PURE__*/_jsx(ToolbarVisibility, {
hideToolbar: hideToolbar,
toolbarBottom: toolbarBottom,
prefixCls: prefixCls,
overflow: overflow,
placement: "top"
}), /*#__PURE__*/_jsxs("div", {
className: prefixCls + "-content",
children: [/(edit|live)/.test(state.preview || '') && /*#__PURE__*/_jsx(TextArea, _extends({
className: prefixCls + "-input",
prefixCls: prefixCls,
autoFocus: autoFocus
}, textareaProps, {
onChange: changeHandle,
renderTextarea: (components == null ? void 0 : components.textarea) || renderTextarea,
onScroll: e => handleScroll(e, 'text')
})), /(live|preview)/.test(state.preview || '') && mdPreview]
}), visibleDragbar && !state.fullscreen && /*#__PURE__*/_jsx(DragBar, {
prefixCls: prefixCls,
height: state.height,
maxHeight: maxHeight,
minHeight: minHeight,
onChange: dragBarChange
}), /*#__PURE__*/_jsx(ToolbarVisibility, {
hideToolbar: hideToolbar,
toolbarBottom: toolbarBottom,
prefixCls: prefixCls,
overflow: overflow,
placement: "bottom"
})]
}))
});
});
var Editor = InternalMDEditor;
Editor.Markdown = MarkdownPreview;
export default Editor;