@seafile/seafile-editor 1.0.31 → 1.0.32-10
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.
- package/dist/constants/index.js +1 -0
- package/dist/containers/hotkeys-helper/index.js +2 -1
- package/dist/containers/outline/index.js +2 -1
- package/dist/editors/simple-slate-editor /index.js +2 -1
- package/dist/editors/slate-editor/index.js +2 -1
- package/dist/editors/slate-viewer/index.js +2 -1
- package/dist/extension/commons/menu/menu-drop-down.js +3 -2
- package/dist/extension/commons/menu/menu-item.js +2 -1
- package/dist/extension/core/utils/index.js +2 -1
- package/dist/extension/plugins/column/render-elem.js +2 -1
- package/dist/extension/plugins/formula/menu/formula-modal.js +2 -1
- package/dist/extension/plugins/header/menu/index.js +2 -1
- package/dist/extension/plugins/header/plugin.js +10 -6
- package/dist/extension/plugins/image/menu/image-menu-dialog.js +2 -1
- package/dist/extension/plugins/image/menu/image-menu-popover.js +2 -1
- package/dist/extension/plugins/image/render-element/index.js +2 -1
- package/dist/extension/plugins/link/menu/link-modal.js +2 -1
- package/dist/extension/plugins/link/render-elem/link-popover.js +2 -1
- package/dist/extension/plugins/markdown/plugin.js +0 -2
- package/dist/extension/plugins/table/render-elem/context-menu.js +2 -1
- package/dist/index.js +4 -1
- package/dist/pages/email-editor-dialog/index.js +131 -0
- package/dist/pages/longtext-editor-dialog/browser-tip.js +16 -0
- package/dist/pages/longtext-editor-dialog/index.js +145 -0
- package/dist/pages/longtext-editor-dialog/longtext-modal.js +34 -0
- package/dist/pages/longtext-editor-dialog/style.css +137 -0
- package/dist/pages/markdown-preview/index.js +59 -0
- package/dist/pages/markdown-preview/style.css +30 -0
- package/dist/utils/get-browser-Info.js +29 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const TRANSLATE_NAMESPACE = 'seafile-editor';
|
|
@@ -4,11 +4,12 @@ import ClassifyHotkeys from './classify-hotkeys';
|
|
|
4
4
|
import { HELPER_HOTKEYS } from '../../constants/hot-keys';
|
|
5
5
|
import { EXTERNAL_EVENTS } from '../../constants/event-types';
|
|
6
6
|
import EventBus from '../../utils/event-bus';
|
|
7
|
+
import { TRANSLATE_NAMESPACE } from '../../constants';
|
|
7
8
|
import './style.css';
|
|
8
9
|
export default function HotkeysHelper() {
|
|
9
10
|
const {
|
|
10
11
|
t
|
|
11
|
-
} = useTranslation();
|
|
12
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
12
13
|
const useHelp = t('userHelp', {
|
|
13
14
|
returnObjects: true
|
|
14
15
|
});
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import OutlineItem from './outline-item';
|
|
4
4
|
import { useScrollContext } from '../../hooks/use-scroll-context';
|
|
5
|
+
import { TRANSLATE_NAMESPACE } from '../../constants';
|
|
5
6
|
import './style.css';
|
|
6
7
|
const getHeaderList = children => {
|
|
7
8
|
const headerList = [];
|
|
@@ -18,7 +19,7 @@ const Outline = _ref => {
|
|
|
18
19
|
} = _ref;
|
|
19
20
|
const {
|
|
20
21
|
t
|
|
21
|
-
} = useTranslation();
|
|
22
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
22
23
|
const scrollRef = useScrollContext();
|
|
23
24
|
const [headerList, setHeaderList] = useState([]);
|
|
24
25
|
const [activeId, setActiveId] = useState('');
|
|
@@ -66,7 +66,8 @@ export default function SimpleSlateEditor(_ref) {
|
|
|
66
66
|
return () => {
|
|
67
67
|
editor.selection = null;
|
|
68
68
|
};
|
|
69
|
-
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
}, []);
|
|
70
71
|
return /*#__PURE__*/React.createElement("div", {
|
|
71
72
|
className: "sf-simple-slate-editor-container"
|
|
72
73
|
}, /*#__PURE__*/React.createElement(Toolbar, {
|
|
@@ -71,7 +71,8 @@ export default function SlateEditor(_ref) {
|
|
|
71
71
|
return () => {
|
|
72
72
|
editor.selection = null;
|
|
73
73
|
};
|
|
74
|
-
|
|
74
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
|
+
}, []);
|
|
75
76
|
return /*#__PURE__*/React.createElement("div", {
|
|
76
77
|
className: "sf-slate-editor-container"
|
|
77
78
|
}, /*#__PURE__*/React.createElement(Toolbar, {
|
|
@@ -27,7 +27,8 @@ export default function SlateViewer(_ref) {
|
|
|
27
27
|
return () => {
|
|
28
28
|
editor.selection = null;
|
|
29
29
|
};
|
|
30
|
-
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
}, []);
|
|
31
32
|
return /*#__PURE__*/React.createElement(Slate, {
|
|
32
33
|
editor: editor,
|
|
33
34
|
initialValue: value
|
|
@@ -2,6 +2,7 @@ import React, { useRef, useState, Fragment, useCallback, useMemo } from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
import Tooltip from '../tooltip';
|
|
5
|
+
import { TRANSLATE_NAMESPACE } from '../../../constants';
|
|
5
6
|
const MenuDropDown = props => {
|
|
6
7
|
const {
|
|
7
8
|
readonly,
|
|
@@ -19,7 +20,7 @@ const MenuDropDown = props => {
|
|
|
19
20
|
const menuDropdownRef = useRef();
|
|
20
21
|
const {
|
|
21
22
|
t
|
|
22
|
-
} = useTranslation();
|
|
23
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
23
24
|
const containerId = useMemo(() => "menu-dropdown-".concat(id), [id]);
|
|
24
25
|
const handleHideDropDownList = useCallback(e => {
|
|
25
26
|
var _menuDropdownRef$curr;
|
|
@@ -46,7 +47,7 @@ const MenuDropDown = props => {
|
|
|
46
47
|
}, /*#__PURE__*/React.createElement("div", {
|
|
47
48
|
ref: menuItemRef,
|
|
48
49
|
id: containerId,
|
|
49
|
-
className: classnames(className, 'menu-group-item sf-menu-with-dropdown', {
|
|
50
|
+
className: classnames(className, 'sf-menu-group-item sf-menu-with-dropdown', {
|
|
50
51
|
'header-popover-showed': isShowDropDown,
|
|
51
52
|
'header-toggle-disabled': isDisabled
|
|
52
53
|
}),
|
|
@@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
import Tooltip from '../tooltip';
|
|
5
|
+
import { TRANSLATE_NAMESPACE } from '../../../constants';
|
|
5
6
|
const MenuItem = _ref => {
|
|
6
7
|
let {
|
|
7
8
|
disabled,
|
|
@@ -16,7 +17,7 @@ const MenuItem = _ref => {
|
|
|
16
17
|
} = _ref;
|
|
17
18
|
const {
|
|
18
19
|
t
|
|
19
|
-
} = useTranslation();
|
|
20
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
20
21
|
const onClick = useCallback(event => {
|
|
21
22
|
if (disabled) return;
|
|
22
23
|
onMouseDown(event, type);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import slugid from 'slugid';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { HEADER1, PARAGRAPH } from '../../constants/element-types';
|
|
4
|
+
import { TRANSLATE_NAMESPACE } from '../../../constants';
|
|
4
5
|
export const match = (node, path, predicate) => {
|
|
5
6
|
if (!predicate) return true;
|
|
6
7
|
if (typeof predicate === 'object') {
|
|
@@ -89,7 +90,7 @@ export const Placeholder = props => {
|
|
|
89
90
|
} = props;
|
|
90
91
|
const {
|
|
91
92
|
t
|
|
92
|
-
} = useTranslation();
|
|
93
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
93
94
|
return /*#__PURE__*/React.createElement("span", {
|
|
94
95
|
style: {
|
|
95
96
|
position: 'absolute',
|
|
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
4
4
|
import { Select } from '../../commons';
|
|
5
5
|
import { COLUMNS_ICON_CONFIG } from './constants/column';
|
|
6
6
|
import { getColumnByKey, setSeaTableColumn } from './helpers';
|
|
7
|
+
import { TRANSLATE_NAMESPACE } from '../../../constants';
|
|
7
8
|
const NOT_SUPPORT_COLUMN_TYPES = ['button', 'file'];
|
|
8
9
|
const Column = _ref => {
|
|
9
10
|
let {
|
|
@@ -15,7 +16,7 @@ const Column = _ref => {
|
|
|
15
16
|
const isSelected = useSelected();
|
|
16
17
|
const {
|
|
17
18
|
t
|
|
18
|
-
} = useTranslation();
|
|
19
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
19
20
|
const columns = useMemo(() => {
|
|
20
21
|
if (!editor.columns) return [];
|
|
21
22
|
return editor.columns.filter(column => !NOT_SUPPORT_COLUMN_TYPES.includes(column.type));
|
|
@@ -3,6 +3,7 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'react
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { insertFormula, updateFormula } from '../helper';
|
|
5
5
|
import { getAboveBlockNode } from '../../../core';
|
|
6
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
6
7
|
const FormulaModal = _ref => {
|
|
7
8
|
let {
|
|
8
9
|
editor,
|
|
@@ -16,7 +17,7 @@ const FormulaModal = _ref => {
|
|
|
16
17
|
const formulaPreviewRef = useRef(null);
|
|
17
18
|
const {
|
|
18
19
|
t
|
|
19
|
-
} = useTranslation(
|
|
20
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
20
21
|
|
|
21
22
|
// record current selection position
|
|
22
23
|
useEffect(() => {
|
|
@@ -6,6 +6,7 @@ import Tooltip from '../../../commons/tooltip';
|
|
|
6
6
|
import { MAC_HOTKEYS_TIP_HEADER, WIN_HOTKEYS_EVENT_HEADER } from '../../../constants/keyboard';
|
|
7
7
|
import { ELementTypes, HEADERS, HEADER_TITLE_MAP } from '../../../constants';
|
|
8
8
|
import './style.css';
|
|
9
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
9
10
|
const headerPopoverShowerList = [ELementTypes.PARAGRAPH, ...HEADERS];
|
|
10
11
|
const HeaderMenu = _ref => {
|
|
11
12
|
let {
|
|
@@ -17,7 +18,7 @@ const HeaderMenu = _ref => {
|
|
|
17
18
|
const headerPopoverRef = useRef();
|
|
18
19
|
const {
|
|
19
20
|
t
|
|
20
|
-
} = useTranslation();
|
|
21
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
21
22
|
const currentHeaderType = getHeaderType(editor);
|
|
22
23
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
24
|
const isDisabled = useMemo(() => isMenuDisabled(editor, readonly), [editor.selection, readonly]);
|
|
@@ -68,8 +68,12 @@ const withHeader = editor => {
|
|
|
68
68
|
insertBreak();
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
-
newEditor.deleteBackward =
|
|
72
|
-
const
|
|
71
|
+
newEditor.deleteBackward = unit => {
|
|
72
|
+
const {
|
|
73
|
+
selection
|
|
74
|
+
} = editor;
|
|
75
|
+
if (!selection) return deleteBackward(unit);
|
|
76
|
+
const [headerEntry] = Editor.nodes(newEditor, {
|
|
73
77
|
match: n => {
|
|
74
78
|
if (!Element.isElement(n)) return false;
|
|
75
79
|
if (n.type.startsWith(ELementTypes.HEADER)) return true;
|
|
@@ -78,16 +82,16 @@ const withHeader = editor => {
|
|
|
78
82
|
// Matches nodes whose node.type starts with header
|
|
79
83
|
universal: true
|
|
80
84
|
});
|
|
81
|
-
if (!
|
|
82
|
-
deleteBackward(
|
|
85
|
+
if (!headerEntry) {
|
|
86
|
+
deleteBackward(unit);
|
|
83
87
|
return false;
|
|
84
88
|
}
|
|
85
|
-
const isAtLineStart = isSelectionAtLineStart(editor,
|
|
89
|
+
const isAtLineStart = isSelectionAtLineStart(editor, headerEntry[1]);
|
|
86
90
|
if (isAtLineStart) {
|
|
87
91
|
setHeaderType(editor, ELementTypes.PARAGRAPH);
|
|
88
92
|
return true;
|
|
89
93
|
}
|
|
90
|
-
return deleteBackward(
|
|
94
|
+
return deleteBackward(unit);
|
|
91
95
|
};
|
|
92
96
|
newEditor.insertFragment = data => {
|
|
93
97
|
var _data$, _data$$children;
|
|
@@ -3,6 +3,7 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Form, FormGroup, La
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import isUrl from 'is-url';
|
|
5
5
|
import { insertImage } from '../helper';
|
|
6
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
6
7
|
const ImageMenuInsertInternetDialog = _ref => {
|
|
7
8
|
let {
|
|
8
9
|
editor,
|
|
@@ -13,7 +14,7 @@ const ImageMenuInsertInternetDialog = _ref => {
|
|
|
13
14
|
const imgUrlInputRef = useRef(null);
|
|
14
15
|
const {
|
|
15
16
|
t
|
|
16
|
-
} = useTranslation();
|
|
17
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
17
18
|
const isCommitBtnDisabled = useMemo(() => {
|
|
18
19
|
if (imageUrl.length === 0) return true;
|
|
19
20
|
if (!isUrl(imageUrl)) return true;
|
|
@@ -5,6 +5,7 @@ import EventBus from '../../../../utils/event-bus';
|
|
|
5
5
|
import { EXTERNAL_EVENTS } from '../../../../constants/event-types';
|
|
6
6
|
import { handleUpdateImage } from '../helper';
|
|
7
7
|
import { focusEditor } from '../../../core';
|
|
8
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
8
9
|
import './style.css';
|
|
9
10
|
const ImageMenuPopover = _ref => {
|
|
10
11
|
let {
|
|
@@ -15,7 +16,7 @@ const ImageMenuPopover = _ref => {
|
|
|
15
16
|
const [isShowInternetImageModal, setIsShowInternetImageModal] = useState(false);
|
|
16
17
|
const {
|
|
17
18
|
t
|
|
18
|
-
} = useTranslation();
|
|
19
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
19
20
|
const handleInsertNetworkImage = e => {
|
|
20
21
|
e.nativeEvent.stopImmediatePropagation();
|
|
21
22
|
e.stopPropagation();
|
|
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
import classNames from 'classnames';
|
|
6
6
|
import { updateImage } from '../helper';
|
|
7
7
|
import ImagePreviewer from './image-previewer';
|
|
8
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
8
9
|
import './style.css';
|
|
9
10
|
const renderImage = (_ref, editor) => {
|
|
10
11
|
var _element$data, _element$data2, _element$data3;
|
|
@@ -21,7 +22,7 @@ const renderImage = (_ref, editor) => {
|
|
|
21
22
|
});
|
|
22
23
|
const {
|
|
23
24
|
t
|
|
24
|
-
} = useTranslation();
|
|
25
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
25
26
|
const imgRef = useRef(null);
|
|
26
27
|
const resizerRef = useRef();
|
|
27
28
|
const isSelected = useSelected();
|
|
@@ -3,6 +3,7 @@ import { Button, Form, FormFeedback, FormGroup, Input, Label, Modal, ModalBody,
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { insertLink, isLinkType, updateLink } from '../helper';
|
|
5
5
|
import { isUrl } from '../../../../utils/common';
|
|
6
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
6
7
|
const LinkModal = _ref => {
|
|
7
8
|
let {
|
|
8
9
|
editor,
|
|
@@ -21,7 +22,7 @@ const LinkModal = _ref => {
|
|
|
21
22
|
const linkAddressRef = useRef(null);
|
|
22
23
|
const {
|
|
23
24
|
t
|
|
24
|
-
} = useTranslation();
|
|
25
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
25
26
|
const isSubmitDisabled = useMemo(() => {
|
|
26
27
|
const isFormDataEmpty = Object.values(formData).some(value => value.length === 0);
|
|
27
28
|
if (isFormDataEmpty) return true;
|
|
@@ -5,6 +5,7 @@ import EventBus from '../../../../utils/event-bus';
|
|
|
5
5
|
import { getLinkInfo, unWrapLinkNode } from '../helper';
|
|
6
6
|
import { isUrl } from '../../../../utils/common';
|
|
7
7
|
import { INTERNAL_EVENTS } from '../../../../constants/event-types';
|
|
8
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
8
9
|
import './style.css';
|
|
9
10
|
const LinkPopover = _ref => {
|
|
10
11
|
let {
|
|
@@ -15,7 +16,7 @@ const LinkPopover = _ref => {
|
|
|
15
16
|
} = _ref;
|
|
16
17
|
const {
|
|
17
18
|
t
|
|
18
|
-
} = useTranslation();
|
|
19
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
19
20
|
useEffect(() => {
|
|
20
21
|
return () => {
|
|
21
22
|
// unregister click event before unmount
|
|
@@ -88,8 +88,6 @@ const withMarkDown = editor => {
|
|
|
88
88
|
const restStr = beforeText === null || beforeText === void 0 ? void 0 : beforeText.slice(0, beforeText.length - 3);
|
|
89
89
|
const startOffset = restStr === null || restStr === void 0 ? void 0 : restStr.lastIndexOf('***');
|
|
90
90
|
const endOffset = (beforeText === null || beforeText === void 0 ? void 0 : beforeText.lastIndexOf('***')) + 3;
|
|
91
|
-
console.log(startOffset);
|
|
92
|
-
console.log(endOffset);
|
|
93
91
|
if (startOffset === -1) {
|
|
94
92
|
return insertText(text);
|
|
95
93
|
}
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { insertColumn, insertRow, removeColumn, removeRow } from '../table-operations';
|
|
4
4
|
import { INSERT_POSITION } from '../../../constants';
|
|
5
|
+
import { TRANSLATE_NAMESPACE } from '../../../../constants';
|
|
5
6
|
const ContextMenu = _ref => {
|
|
6
7
|
let {
|
|
7
8
|
position,
|
|
@@ -12,7 +13,7 @@ const ContextMenu = _ref => {
|
|
|
12
13
|
const contextMenuRef = useRef(null);
|
|
13
14
|
const {
|
|
14
15
|
t
|
|
15
|
-
} = useTranslation();
|
|
16
|
+
} = useTranslation(TRANSLATE_NAMESPACE);
|
|
16
17
|
useLayoutEffect(() => {
|
|
17
18
|
generateContextMenuStyle(position);
|
|
18
19
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,13 @@ import RichMarkdownEditor from './pages/rich-markdown-editor';
|
|
|
4
4
|
import MarkdownEditor from './pages/markdown-editor';
|
|
5
5
|
import MarkdownViewer from './pages/markdown-view';
|
|
6
6
|
import SimpleEditor from './pages/simple-editor';
|
|
7
|
+
import EmailEditorDialog from './pages/email-editor-dialog';
|
|
8
|
+
import LongTextEditorDialog from './pages/longtext-editor-dialog';
|
|
9
|
+
import MarkdownPreview from './pages/markdown-preview';
|
|
7
10
|
import SeaTableEditor from './pages/seatable-editor';
|
|
8
11
|
import SeaTableViewer from './pages/seatable-viewer';
|
|
9
12
|
import EventBus from './utils/event-bus';
|
|
10
13
|
import { mdStringToSlate, slateToMdString, deserializeHtml, processor } from './slate-convert';
|
|
11
14
|
import { replaceColumnData } from './utils/replace-slate-nodes';
|
|
12
15
|
import getPreviewContent from './utils/get-preview-content';
|
|
13
|
-
export { MarkdownEditor, PlainMarkdownEditor, RichMarkdownEditor, MarkdownViewer, SimpleEditor, SeaTableEditor, SeaTableViewer, EXTERNAL_EVENTS, EventBus, mdStringToSlate, slateToMdString, deserializeHtml, processor, replaceColumnData, getPreviewContent };
|
|
16
|
+
export { MarkdownEditor, PlainMarkdownEditor, RichMarkdownEditor, MarkdownViewer, SimpleEditor, SeaTableEditor, SeaTableViewer, EmailEditorDialog, LongTextEditorDialog, MarkdownPreview, EXTERNAL_EVENTS, EventBus, mdStringToSlate, slateToMdString, deserializeHtml, processor, replaceColumnData, getPreviewContent };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import getBrowserInfo from '../../utils/get-browser-Info';
|
|
4
|
+
import LongTextModal from '../longtext-editor-dialog/longtext-modal';
|
|
5
|
+
import BrowserTip from '../longtext-editor-dialog/browser-tip';
|
|
6
|
+
import SeaTableEditor from '../seatable-editor';
|
|
7
|
+
import '../longtext-editor-dialog/style.css';
|
|
8
|
+
export default function EmailEditorDialog(_ref) {
|
|
9
|
+
let {
|
|
10
|
+
lang,
|
|
11
|
+
readOnly = false,
|
|
12
|
+
headerName,
|
|
13
|
+
value,
|
|
14
|
+
columns,
|
|
15
|
+
autoSave = true,
|
|
16
|
+
saveDelay = 60000,
|
|
17
|
+
isCheckBrowser = false,
|
|
18
|
+
editorApi,
|
|
19
|
+
onSaveEditorValue,
|
|
20
|
+
onEditorValueChanged,
|
|
21
|
+
onCloseEditorDialog
|
|
22
|
+
} = _ref;
|
|
23
|
+
const editorRef = useRef(null);
|
|
24
|
+
const [isValueChanged, setValueChanged] = useState(false);
|
|
25
|
+
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
26
|
+
const [dialogStyle, setDialogStyle] = useState({});
|
|
27
|
+
const onUpdateEditorValue = useCallback(() => {
|
|
28
|
+
var _editorRef$current;
|
|
29
|
+
if (!isValueChanged || readOnly) return;
|
|
30
|
+
const slateNodes = (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.getSlateValue();
|
|
31
|
+
onSaveEditorValue(slateNodes);
|
|
32
|
+
setValueChanged(false);
|
|
33
|
+
}, [isValueChanged, onSaveEditorValue, readOnly]);
|
|
34
|
+
const onCloseToggle = useCallback(() => {
|
|
35
|
+
onUpdateEditorValue();
|
|
36
|
+
onCloseEditorDialog();
|
|
37
|
+
}, [onCloseEditorDialog, onUpdateEditorValue]);
|
|
38
|
+
const onHotKey = useCallback(event => {
|
|
39
|
+
if (event.keyCode === 27) {
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
onCloseToggle();
|
|
43
|
+
}
|
|
44
|
+
}, [onCloseToggle]);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
let timer = null;
|
|
47
|
+
if (autoSave) {
|
|
48
|
+
timer = setTimeout(() => {
|
|
49
|
+
onUpdateEditorValue();
|
|
50
|
+
}, saveDelay);
|
|
51
|
+
}
|
|
52
|
+
document.addEventListener('keydown', onHotKey);
|
|
53
|
+
return () => {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
document.removeEventListener('keydown', onHotKey);
|
|
56
|
+
};
|
|
57
|
+
}, [autoSave, saveDelay, onUpdateEditorValue, onHotKey]);
|
|
58
|
+
const {
|
|
59
|
+
isValidBrowser,
|
|
60
|
+
isWindowsWechat
|
|
61
|
+
} = useMemo(() => {
|
|
62
|
+
return getBrowserInfo(isCheckBrowser);
|
|
63
|
+
}, [isCheckBrowser]);
|
|
64
|
+
const onFullScreenToggle = useCallback(() => {
|
|
65
|
+
let containerStyle = {};
|
|
66
|
+
if (!isFullScreen) {
|
|
67
|
+
containerStyle = {
|
|
68
|
+
width: '100%',
|
|
69
|
+
height: '100%',
|
|
70
|
+
top: 0,
|
|
71
|
+
border: 'none'
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
setIsFullScreen(!isFullScreen);
|
|
75
|
+
setDialogStyle(containerStyle);
|
|
76
|
+
}, [isFullScreen]);
|
|
77
|
+
const onContentChanged = useCallback(() => {
|
|
78
|
+
// delay to update editor's content
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
// update parent's component cache value
|
|
81
|
+
if (onEditorValueChanged && typeof onEditorValueChanged === 'function') {
|
|
82
|
+
var _editorRef$current2;
|
|
83
|
+
const slateNodes = (_editorRef$current2 = editorRef.current) === null || _editorRef$current2 === void 0 ? void 0 : _editorRef$current2.getSlateValue();
|
|
84
|
+
onEditorValueChanged(slateNodes);
|
|
85
|
+
}
|
|
86
|
+
setValueChanged(true);
|
|
87
|
+
}, 0);
|
|
88
|
+
}, [onEditorValueChanged]);
|
|
89
|
+
const onContainerKeyDown = event => {
|
|
90
|
+
event.stopPropagation();
|
|
91
|
+
onHotKey(event);
|
|
92
|
+
};
|
|
93
|
+
const headerClass = classNames('longtext-header-container', {
|
|
94
|
+
'longtext-header-container-border': readOnly || isWindowsWechat
|
|
95
|
+
});
|
|
96
|
+
const contentClass = classNames('longtext-content-container', {
|
|
97
|
+
'longtext-container-scroll': readOnly || isWindowsWechat
|
|
98
|
+
});
|
|
99
|
+
return /*#__PURE__*/React.createElement(LongTextModal, {
|
|
100
|
+
onModalClick: onCloseToggle
|
|
101
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
102
|
+
style: dialogStyle,
|
|
103
|
+
className: "longtext-dialog-container"
|
|
104
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
105
|
+
className: headerClass
|
|
106
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
107
|
+
className: "longtext-header"
|
|
108
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
109
|
+
className: "longtext-header-name"
|
|
110
|
+
}, headerName), /*#__PURE__*/React.createElement("div", {
|
|
111
|
+
className: "longtext-header-tool"
|
|
112
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
113
|
+
onClick: onFullScreenToggle,
|
|
114
|
+
className: "longtext-header-tool-item mr-1 dtable-font dtable-icon-full-screen ".concat(isFullScreen ? 'long-text-full-screen' : '')
|
|
115
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
116
|
+
onClick: onCloseToggle,
|
|
117
|
+
className: "longtext-header-tool-item dtable-font dtable-icon-x"
|
|
118
|
+
}))), !isValidBrowser && /*#__PURE__*/React.createElement(BrowserTip, {
|
|
119
|
+
lang: lang,
|
|
120
|
+
isWindowsWechat: isWindowsWechat
|
|
121
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
122
|
+
onKeyDown: onContainerKeyDown,
|
|
123
|
+
className: contentClass
|
|
124
|
+
}, /*#__PURE__*/React.createElement(SeaTableEditor, {
|
|
125
|
+
ref: editorRef,
|
|
126
|
+
value: value,
|
|
127
|
+
columns: columns,
|
|
128
|
+
editorApi: editorApi,
|
|
129
|
+
onContentChanged: onContentChanged
|
|
130
|
+
}))));
|
|
131
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export default function BrowserTip(_ref) {
|
|
3
|
+
let {
|
|
4
|
+
lang,
|
|
5
|
+
isWindowsWechat
|
|
6
|
+
} = _ref;
|
|
7
|
+
if (lang !== 'zh-cn') return null;
|
|
8
|
+
const msg = isWindowsWechat ? '你使用的微信存在兼容性问题,仅以只读模式预览。' : '你当前使用的浏览器可能存在兼容性问题。';
|
|
9
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
10
|
+
className: "browser-tip"
|
|
11
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
12
|
+
className: "browser-tip__icon dtable-font dtable-icon-description"
|
|
13
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
14
|
+
className: "browser-tip__message"
|
|
15
|
+
}, "".concat(msg, "\u8BF7\u8BD5\u8BD5\u4EE5\u4E0B\u6D4F\u89C8\u5668: Chrome \u6D4F\u89C8\u5668\u6700\u65B0\u7248\uFF0C360 \u6781\u901F\u7248\uFF0CMicrosoft Edge \u6700\u65B0\u7248")));
|
|
16
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import SimpleEditor from '../simple-editor';
|
|
4
|
+
import getPreviewContent from '../../utils/get-preview-content';
|
|
5
|
+
import getBrowserInfo from '../../utils/get-browser-Info';
|
|
6
|
+
import LongTextModal from './longtext-modal';
|
|
7
|
+
import BrowserTip from './browser-tip';
|
|
8
|
+
import MarkdownPreview from '../markdown-preview';
|
|
9
|
+
import './style.css';
|
|
10
|
+
export default function LongTextEditorDialog(_ref) {
|
|
11
|
+
let {
|
|
12
|
+
lang,
|
|
13
|
+
readOnly,
|
|
14
|
+
headerName,
|
|
15
|
+
value,
|
|
16
|
+
autoSave = true,
|
|
17
|
+
saveDelay = 60000,
|
|
18
|
+
isCheckBrowser = false,
|
|
19
|
+
editorApi,
|
|
20
|
+
onSaveEditorValue,
|
|
21
|
+
onEditorValueChanged,
|
|
22
|
+
onCloseEditorDialog
|
|
23
|
+
} = _ref;
|
|
24
|
+
const editorRef = useRef(null);
|
|
25
|
+
const [isValueChanged, setValueChanged] = useState(false);
|
|
26
|
+
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
27
|
+
const [dialogStyle, setDialogStyle] = useState({});
|
|
28
|
+
const onUpdateEditorValue = useCallback(() => {
|
|
29
|
+
var _editorRef$current, _editorRef$current2;
|
|
30
|
+
if (!isValueChanged || readOnly) return;
|
|
31
|
+
const markdownString = (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.getValue();
|
|
32
|
+
const slateNodes = (_editorRef$current2 = editorRef.current) === null || _editorRef$current2 === void 0 ? void 0 : _editorRef$current2.getSlateValue();
|
|
33
|
+
const value = getPreviewContent(slateNodes);
|
|
34
|
+
onSaveEditorValue({
|
|
35
|
+
...value,
|
|
36
|
+
text: markdownString
|
|
37
|
+
});
|
|
38
|
+
setValueChanged(false);
|
|
39
|
+
}, [isValueChanged, onSaveEditorValue, readOnly]);
|
|
40
|
+
const onCloseToggle = useCallback(() => {
|
|
41
|
+
onUpdateEditorValue();
|
|
42
|
+
onCloseEditorDialog();
|
|
43
|
+
}, [onCloseEditorDialog, onUpdateEditorValue]);
|
|
44
|
+
const onHotKey = useCallback(event => {
|
|
45
|
+
if (event.keyCode === 27) {
|
|
46
|
+
event.stopPropagation();
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
onCloseToggle();
|
|
49
|
+
}
|
|
50
|
+
}, [onCloseToggle]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
let timer = null;
|
|
53
|
+
if (autoSave) {
|
|
54
|
+
timer = setTimeout(() => {
|
|
55
|
+
onUpdateEditorValue();
|
|
56
|
+
}, saveDelay);
|
|
57
|
+
}
|
|
58
|
+
document.addEventListener('keydown', onHotKey);
|
|
59
|
+
return () => {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
document.removeEventListener('keydown', onHotKey);
|
|
62
|
+
};
|
|
63
|
+
}, [autoSave, saveDelay, onUpdateEditorValue, onHotKey]);
|
|
64
|
+
const {
|
|
65
|
+
isValidBrowser,
|
|
66
|
+
isWindowsWechat
|
|
67
|
+
} = useMemo(() => {
|
|
68
|
+
return getBrowserInfo(isCheckBrowser);
|
|
69
|
+
}, [isCheckBrowser]);
|
|
70
|
+
const onFullScreenToggle = useCallback(() => {
|
|
71
|
+
let containerStyle = {};
|
|
72
|
+
if (!isFullScreen) {
|
|
73
|
+
containerStyle = {
|
|
74
|
+
width: '100%',
|
|
75
|
+
height: '100%',
|
|
76
|
+
top: 0,
|
|
77
|
+
border: 'none'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
setIsFullScreen(!isFullScreen);
|
|
81
|
+
setDialogStyle(containerStyle);
|
|
82
|
+
}, [isFullScreen]);
|
|
83
|
+
const onContentChanged = useCallback(() => {
|
|
84
|
+
// delay to update editor's content
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
// update parent's component cache value
|
|
87
|
+
if (onEditorValueChanged && typeof onEditorValueChanged === 'function') {
|
|
88
|
+
var _editorRef$current3, _editorRef$current4;
|
|
89
|
+
const markdownString = (_editorRef$current3 = editorRef.current) === null || _editorRef$current3 === void 0 ? void 0 : _editorRef$current3.getValue();
|
|
90
|
+
const slateNodes = (_editorRef$current4 = editorRef.current) === null || _editorRef$current4 === void 0 ? void 0 : _editorRef$current4.getSlateValue();
|
|
91
|
+
const value = getPreviewContent(slateNodes);
|
|
92
|
+
onEditorValueChanged({
|
|
93
|
+
...value,
|
|
94
|
+
text: markdownString
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
setValueChanged(true);
|
|
98
|
+
}, 0);
|
|
99
|
+
}, [onEditorValueChanged]);
|
|
100
|
+
const onContainerKeyDown = event => {
|
|
101
|
+
event.stopPropagation();
|
|
102
|
+
onHotKey(event);
|
|
103
|
+
};
|
|
104
|
+
const headerClass = classNames('longtext-header-container', {
|
|
105
|
+
'longtext-header-container-border': readOnly || isWindowsWechat
|
|
106
|
+
});
|
|
107
|
+
const contentClass = classNames('longtext-content-container', {
|
|
108
|
+
'longtext-container-scroll': readOnly || isWindowsWechat
|
|
109
|
+
});
|
|
110
|
+
return /*#__PURE__*/React.createElement(LongTextModal, {
|
|
111
|
+
onModalClick: onCloseToggle
|
|
112
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
113
|
+
style: dialogStyle,
|
|
114
|
+
className: "longtext-dialog-container"
|
|
115
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
116
|
+
className: headerClass
|
|
117
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
118
|
+
className: "longtext-header"
|
|
119
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
120
|
+
className: "longtext-header-name"
|
|
121
|
+
}, headerName), /*#__PURE__*/React.createElement("div", {
|
|
122
|
+
className: "longtext-header-tool"
|
|
123
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
124
|
+
onClick: onFullScreenToggle,
|
|
125
|
+
className: "longtext-header-tool-item mr-1 dtable-font dtable-icon-full-screen ".concat(isFullScreen ? 'long-text-full-screen' : '')
|
|
126
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
127
|
+
onClick: onCloseToggle,
|
|
128
|
+
className: "longtext-header-tool-item dtable-font dtable-icon-x"
|
|
129
|
+
}))), !isValidBrowser && /*#__PURE__*/React.createElement(BrowserTip, {
|
|
130
|
+
lang: lang,
|
|
131
|
+
isWindowsWechat: isWindowsWechat
|
|
132
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
133
|
+
onKeyDown: onContainerKeyDown,
|
|
134
|
+
className: contentClass
|
|
135
|
+
}, !readOnly && !isWindowsWechat && /*#__PURE__*/React.createElement(SimpleEditor, {
|
|
136
|
+
ref: editorRef,
|
|
137
|
+
value: value,
|
|
138
|
+
editorApi: editorApi,
|
|
139
|
+
onContentChanged: onContentChanged
|
|
140
|
+
}), (readOnly || isWindowsWechat) && /*#__PURE__*/React.createElement(MarkdownPreview, {
|
|
141
|
+
isWindowsWechat: isWindowsWechat,
|
|
142
|
+
value: value,
|
|
143
|
+
isShowOutline: false
|
|
144
|
+
}))));
|
|
145
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import ReactDOM from 'react-dom';
|
|
4
|
+
class LongTextModal extends React.Component {
|
|
5
|
+
constructor(props) {
|
|
6
|
+
super(props);
|
|
7
|
+
_defineProperty(this, "onModalClick", e => {
|
|
8
|
+
e && e.stopPropagation();
|
|
9
|
+
const className = e.target.className;
|
|
10
|
+
if (typeof className !== 'string') return;
|
|
11
|
+
if (this.props.onModalClick && (className === 'longtext-modal-wrapper' || className.startsWith('longtext-modal-wrapper'))) {
|
|
12
|
+
this.props.onModalClick();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
const {
|
|
16
|
+
containerClass
|
|
17
|
+
} = this.props;
|
|
18
|
+
this.el = document.createElement('div');
|
|
19
|
+
this.el.className = 'longtext-modal-wrapper';
|
|
20
|
+
this.el.className = "longtext-modal-wrapper ".concat(containerClass || '');
|
|
21
|
+
document.body.appendChild(this.el);
|
|
22
|
+
}
|
|
23
|
+
componentDidMount() {
|
|
24
|
+
this.el.addEventListener('mousedown', this.onModalClick);
|
|
25
|
+
}
|
|
26
|
+
componentWillUnmount() {
|
|
27
|
+
this.el.removeEventListener('mousedown', this.onModalClick);
|
|
28
|
+
document.body.removeChild(this.el);
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
return ReactDOM.createPortal(this.props.children, this.el);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export default LongTextModal;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
.longtext-modal-wrapper {
|
|
2
|
+
position: fixed;
|
|
3
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
4
|
+
left: 0;
|
|
5
|
+
top: 0;
|
|
6
|
+
bottom: 0;
|
|
7
|
+
right: 0;
|
|
8
|
+
z-index: 1070;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.longtext-dialog-container {
|
|
12
|
+
width: 800px;
|
|
13
|
+
border-radius: 3px;
|
|
14
|
+
position: absolute;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
margin-left: 50%;
|
|
18
|
+
transform: translateX(-50%);
|
|
19
|
+
top: 20px;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
bottom: 20px;
|
|
22
|
+
border: 1px solid rgba(0, 0, 0, 0.2);
|
|
23
|
+
background-color: #ffffff;
|
|
24
|
+
background-clip: padding-box;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* header */
|
|
28
|
+
.longtext-header-container {
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
padding: 0 16px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.longtext-header-container.longtext-header-container-border {
|
|
34
|
+
border-bottom: 1px solid #e9ecef;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.longtext-header-container .longtext-header {
|
|
38
|
+
height: 36px;
|
|
39
|
+
line-height: 36px;
|
|
40
|
+
display: flex;
|
|
41
|
+
justify-content: space-between;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.longtext-header-container .longtext-header-name {
|
|
45
|
+
font-weight: 700;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.longtext-header-container .longtext-header-tool {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
flex-direction: row;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.longtext-header-container .longtext-header-tool-item {
|
|
55
|
+
font-size: 16px;
|
|
56
|
+
font-weight: 700;
|
|
57
|
+
color: #000;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
opacity: 0.5;
|
|
60
|
+
width: 24px;
|
|
61
|
+
height: 24px;
|
|
62
|
+
text-align: center;
|
|
63
|
+
display: block;
|
|
64
|
+
line-height: 24px;
|
|
65
|
+
border-radius: 3px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.longtext-header-container .longtext-header-tool-item.long-text-full-screen {
|
|
69
|
+
background-color: #e0e0e0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.longtext-header-container .longtext-header-tool-item:not(.long-text-full-screen):hover {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* tip message */
|
|
77
|
+
.longtext-header-container .browser-tip {
|
|
78
|
+
margin-bottom: 8px;
|
|
79
|
+
height: 12px;
|
|
80
|
+
display: flex;
|
|
81
|
+
font-size: 13px;
|
|
82
|
+
color: #f25041;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.longtext-header-container .browser-tip__icon {
|
|
86
|
+
margin-right: 4px;
|
|
87
|
+
display: inline-block;
|
|
88
|
+
line-height: 12px;
|
|
89
|
+
transform: scale(0.8);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.longtext-header-container .browser-tip__message {
|
|
93
|
+
line-height: 12px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* content */
|
|
97
|
+
.longtext-content-container {
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
flex: 1 1 auto;
|
|
100
|
+
height: fit-content;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.longtext-content-container.longtext-content-container-scroll {
|
|
104
|
+
overflow-y: auto;
|
|
105
|
+
height: fit-content;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.longtext-content-container .sf-slate-editor-toolbar {
|
|
109
|
+
height: 34px;
|
|
110
|
+
border-top: 1px solid #e9ecef;
|
|
111
|
+
border-bottom: 1px solid #e9ecef;
|
|
112
|
+
box-shadow: none;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.longtext-content-container .sf-slate-editor-toolbar .sf-menu-group {
|
|
116
|
+
padding: 4px 0 4px 8px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.longtext-content-container .sf-simple-slate-editor-container {
|
|
120
|
+
border: none;
|
|
121
|
+
height: 100%;
|
|
122
|
+
width: 100%;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.longtext-content-container .sf-simple-slate-editor-container .article {
|
|
126
|
+
padding: 16px;
|
|
127
|
+
height: auto;
|
|
128
|
+
min-height: 100% !important;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.longtext-content-container .sf-simple-slate-editor-container .article>div :first-child {
|
|
132
|
+
margin-top: 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.longtext-content-container .article h2 {
|
|
136
|
+
border-bottom: none;
|
|
137
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import Loading from '../../containers/loading';
|
|
4
|
+
import MarkdownViewer from '../markdown-view';
|
|
5
|
+
import { processor } from '../../slate-convert';
|
|
6
|
+
import './style.css';
|
|
7
|
+
// Windows old Wechat (3.0 or earlier) inner core is chrome 53 and don't support ECMA6, can't use seafile-editor markdownViewer
|
|
8
|
+
// Windows new Wechat (lastest version 3.3.5) support seafile-editor markdownViewer
|
|
9
|
+
// so use dangerouslySetInnerHTML to preview
|
|
10
|
+
class MarkdownPreview extends React.PureComponent {
|
|
11
|
+
constructor(props) {
|
|
12
|
+
super(props);
|
|
13
|
+
_defineProperty(this, "convertMarkdown", mdFile => {
|
|
14
|
+
processor.process(mdFile).then(result => {
|
|
15
|
+
let innerHtml = String(result).replace(/<a /ig, '<a target="_blank" tabindex="-1"');
|
|
16
|
+
this.setState({
|
|
17
|
+
innerHtml
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
this.state = {
|
|
22
|
+
innerHtml: null
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
componentDidMount() {
|
|
26
|
+
const {
|
|
27
|
+
isWindowsWechat,
|
|
28
|
+
value
|
|
29
|
+
} = this.props;
|
|
30
|
+
if (isWindowsWechat) {
|
|
31
|
+
this.convertMarkdown(value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
render() {
|
|
35
|
+
const {
|
|
36
|
+
isWindowsWechat,
|
|
37
|
+
value,
|
|
38
|
+
isShowOutline
|
|
39
|
+
} = this.props;
|
|
40
|
+
const {
|
|
41
|
+
innerHtml
|
|
42
|
+
} = this.state;
|
|
43
|
+
if (isWindowsWechat && innerHtml === null) {
|
|
44
|
+
return /*#__PURE__*/React.createElement(Loading, null);
|
|
45
|
+
}
|
|
46
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
47
|
+
className: "longtext-preview-container"
|
|
48
|
+
}, isWindowsWechat && /*#__PURE__*/React.createElement("div", {
|
|
49
|
+
className: "article",
|
|
50
|
+
dangerouslySetInnerHTML: {
|
|
51
|
+
__html: this.state.innerHtml
|
|
52
|
+
}
|
|
53
|
+
}), !isWindowsWechat && /*#__PURE__*/React.createElement(MarkdownViewer, {
|
|
54
|
+
value: value,
|
|
55
|
+
isShowOutline: isShowOutline
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export default MarkdownPreview;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.longtext-preview-container {
|
|
2
|
+
width: 100%;
|
|
3
|
+
height: 100%;
|
|
4
|
+
padding: 16px;
|
|
5
|
+
overflow: auto;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.longtext-preview-container .sf-slate-viewer-scroll-container {
|
|
9
|
+
overflow-y: auto;
|
|
10
|
+
height: fit-content;
|
|
11
|
+
padding: 0;
|
|
12
|
+
background: #fff;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.longtext-preview-container .sf-slate-viewer-article-container {
|
|
16
|
+
height: 100%;
|
|
17
|
+
width: 100%;
|
|
18
|
+
margin: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.longtext-preview-container .sf-slate-viewer-article-container .article {
|
|
22
|
+
padding: 0;
|
|
23
|
+
height: auto;
|
|
24
|
+
min-height: 100% !important;
|
|
25
|
+
border: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.longtext-preview-container .article :first-child {
|
|
29
|
+
margin-top: 0;
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const getBrowserInfo = isCheckBrowser => {
|
|
2
|
+
if (!isCheckBrowser) return {
|
|
3
|
+
isValidBrowser: true,
|
|
4
|
+
isWindowsWechat: false
|
|
5
|
+
};
|
|
6
|
+
let isValidBrowser = false;
|
|
7
|
+
let isWindowsWechat = false;
|
|
8
|
+
if (window.chrome) {
|
|
9
|
+
const appVersion = window.navigator.appVersion;
|
|
10
|
+
const appVersionList = appVersion.split(' ');
|
|
11
|
+
const index = appVersionList.findIndex(version => {
|
|
12
|
+
return version.indexOf('Chrome') >= 0;
|
|
13
|
+
});
|
|
14
|
+
let chromeVersion = appVersionList[index];
|
|
15
|
+
chromeVersion = chromeVersion.slice(chromeVersion.indexOf('/') + 1);
|
|
16
|
+
chromeVersion = parseInt(chromeVersion);
|
|
17
|
+
isValidBrowser = chromeVersion >= 76;
|
|
18
|
+
// Windows Wechat inner core is chrome 53, not support ECMA6, so use readonly mode
|
|
19
|
+
if (chromeVersion === 53 && navigator.appVersion && navigator.appVersion.includes('WindowsWechat')) {
|
|
20
|
+
isValidBrowser = false;
|
|
21
|
+
isWindowsWechat = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
isValidBrowser,
|
|
26
|
+
isWindowsWechat
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export default getBrowserInfo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seafile/seafile-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.32-10",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"@codemirror/lang-markdown": "6.2.3",
|
|
88
88
|
"@codemirror/language-data": "6.3.1",
|
|
89
89
|
"@codemirror/view": "6.22.1",
|
|
90
|
-
"@seafile/react-image-lightbox": "2.0.
|
|
90
|
+
"@seafile/react-image-lightbox": "2.0.5",
|
|
91
91
|
"classnames": "2.3.2",
|
|
92
92
|
"codemirror": "6.0.1",
|
|
93
93
|
"deep-copy": "1.4.2",
|