@seafile/seafile-editor 1.0.4-2 → 1.0.4-20
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/event-types.js +4 -1
- package/dist/editors/plain-markdown-editor/index.js +1 -1
- package/dist/editors/simple-slate-editor /index.js +16 -4
- package/dist/editors/simple-slate-editor /style.css +72 -0
- package/dist/editors/slate-editor/index.js +20 -4
- package/dist/editors/slate-viewer/index.js +12 -3
- package/dist/editors/slate-viewer/style.css +12 -7
- package/dist/extension/constants/index.js +7 -7
- package/dist/extension/core/utils/index.js +12 -1
- package/dist/extension/plugins/image/helper.js +28 -2
- package/dist/extension/plugins/image/menu/image-menu-popover.js +20 -9
- package/dist/extension/plugins/image/menu/index.js +4 -2
- package/dist/extension/plugins/image/menu/style.css +2 -0
- package/dist/extension/plugins/link/helper.js +43 -4
- package/dist/extension/plugins/link/render-elem/index.js +9 -5
- package/dist/extension/toolbar/header-toolbar/index.js +6 -3
- package/dist/hooks/use-insert-image.js +36 -0
- package/dist/pages/markdown-editor.js +7 -5
- package/dist/pages/markdown-view.js +3 -1
- package/dist/pages/rich-markdown-editor.js +15 -6
- package/dist/pages/simple-editor.js +7 -5
- package/dist/slate-convert/md-to-slate/transform.js +14 -3
- package/dist/utils/event-handler.js +4 -0
- package/package.json +1 -1
- package/public/locales/cs/seafile-editor.json +170 -116
- package/public/locales/de/seafile-editor.json +184 -130
- package/public/locales/es/seafile-editor.json +171 -117
- package/public/locales/fr/seafile-editor.json +174 -120
- package/public/locales/it/seafile-editor.json +171 -117
- package/public/locales/ru/seafile-editor.json +171 -117
- package/public/locales/zh-CN/seafile-editor.json +4 -4
- /package/dist/{assets/css/slate-editor.css → editors/slate-editor/style.css} +0 -0
|
@@ -6,5 +6,8 @@ export const INTERNAL_EVENTS = {
|
|
|
6
6
|
ON_OPEN_FORMULA_DIALOG: 'on_open_formula_dialog'
|
|
7
7
|
};
|
|
8
8
|
export const EXTERNAL_EVENTS = {
|
|
9
|
-
ON_HELP_INFO_TOGGLE: 'on_help_info_toggle'
|
|
9
|
+
ON_HELP_INFO_TOGGLE: 'on_help_info_toggle',
|
|
10
|
+
ON_LINK_CLICK: 'on_link_click',
|
|
11
|
+
ON_INSERT_IMAGE: 'on_insert_image',
|
|
12
|
+
INSERT_IMAGE: 'insert_image'
|
|
10
13
|
};
|
|
@@ -29,7 +29,7 @@ class PlainMarkdownEditor extends React.Component {
|
|
|
29
29
|
});
|
|
30
30
|
_defineProperty(this, "updateCode", newCode => {
|
|
31
31
|
this.setContent(newCode);
|
|
32
|
-
this.props.
|
|
32
|
+
this.props.onContentChanged && this.props.onContentChanged(newCode);
|
|
33
33
|
});
|
|
34
34
|
_defineProperty(this, "onEnterLeftPanel", () => {
|
|
35
35
|
this.setState({
|
|
@@ -6,12 +6,13 @@ import EventBus from '../../utils/event-bus';
|
|
|
6
6
|
import EventProxy from '../../utils/event-handler';
|
|
7
7
|
import withPropsEditor from './with-props-editor';
|
|
8
8
|
import { focusEditor } from '../../extension/core';
|
|
9
|
-
import '
|
|
9
|
+
import './style.css';
|
|
10
10
|
export default function SimpleSlateEditor(_ref) {
|
|
11
11
|
let {
|
|
12
12
|
value,
|
|
13
13
|
editorApi,
|
|
14
14
|
onSave,
|
|
15
|
+
onContentChanged,
|
|
15
16
|
isSupportFormula
|
|
16
17
|
} = _ref;
|
|
17
18
|
const [slateValue, setSlateValue] = useState(value);
|
|
@@ -24,10 +25,14 @@ export default function SimpleSlateEditor(_ref) {
|
|
|
24
25
|
}, [editor]);
|
|
25
26
|
const onChange = useCallback(value => {
|
|
26
27
|
setSlateValue(value);
|
|
27
|
-
|
|
28
|
+
const operations = editor.operations;
|
|
29
|
+
const modifyOps = operations.filter(o => o.type !== 'set_selection');
|
|
30
|
+
if (modifyOps.length > 0) {
|
|
31
|
+
onContentChanged && onContentChanged(value);
|
|
32
|
+
}
|
|
28
33
|
const eventBus = EventBus.getInstance();
|
|
29
34
|
eventBus.dispatch('change');
|
|
30
|
-
}, [
|
|
35
|
+
}, [editor.operations, onContentChanged]);
|
|
31
36
|
|
|
32
37
|
// useMount: focus editor
|
|
33
38
|
useEffect(() => {
|
|
@@ -50,8 +55,15 @@ export default function SimpleSlateEditor(_ref) {
|
|
|
50
55
|
};
|
|
51
56
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
57
|
}, []);
|
|
58
|
+
|
|
59
|
+
// willUnmount
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
return () => {
|
|
62
|
+
editor.selection = null;
|
|
63
|
+
};
|
|
64
|
+
});
|
|
53
65
|
return /*#__PURE__*/React.createElement("div", {
|
|
54
|
-
className: "sf-slate-editor-container"
|
|
66
|
+
className: "sf-simple-slate-editor-container"
|
|
55
67
|
}, /*#__PURE__*/React.createElement(Toolbar, {
|
|
56
68
|
editor: editor,
|
|
57
69
|
isSupportFormula: isSupportFormula
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.sf-simple-slate-editor-container {
|
|
2
|
+
flex: 1;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
min-height: 0;
|
|
6
|
+
min-width: 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.sf-simple-slate-editor-container .sf-slate-editor-toolbar {
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: flex-start;
|
|
12
|
+
height: 44px;
|
|
13
|
+
align-items: center;
|
|
14
|
+
padding: 0 10px;
|
|
15
|
+
background-color: #fff;
|
|
16
|
+
user-select: none;
|
|
17
|
+
border-bottom: 1px solid #e5e6e8;
|
|
18
|
+
position: relative;
|
|
19
|
+
z-index: 102;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.sf-simple-slate-editor-container .sf-slate-editor-content {
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: calc(100% - 44px);
|
|
25
|
+
display: flex;
|
|
26
|
+
background: #f5f5f5;
|
|
27
|
+
position: relative;
|
|
28
|
+
min-height: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.sf-simple-slate-editor-container .sf-slate-scroll-container,
|
|
32
|
+
.sf-simple-slate-editor-container .sf-slate-article-container {
|
|
33
|
+
height: 100%;
|
|
34
|
+
width: 100%;
|
|
35
|
+
overflow: auto;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* .sf-simple-slate-editor-container .sf-slate-article-container {
|
|
39
|
+
flex: 1;
|
|
40
|
+
position: relative;
|
|
41
|
+
max-width: 950px;
|
|
42
|
+
min-width: 400px;
|
|
43
|
+
margin: 0 auto;
|
|
44
|
+
padding-top: 20px;
|
|
45
|
+
padding-bottom: 20px;
|
|
46
|
+
} */
|
|
47
|
+
|
|
48
|
+
.sf-simple-slate-editor-container .sf-slate-editor-content .article {
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 10px;
|
|
51
|
+
height: 100%;
|
|
52
|
+
border: none;
|
|
53
|
+
background-color: #fff;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.sf-simple-slate-editor-container .sf-slate-editor-content .article div:first-child {
|
|
57
|
+
outline: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.sf-simple-slate-editor-container ::-webkit-scrollbar{
|
|
61
|
+
width: 8px;
|
|
62
|
+
height: 8px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.sf-simple-slate-editor-container ::-webkit-scrollbar-button {
|
|
66
|
+
display: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.sf-simple-slate-editor-container ::-webkit-scrollbar-thumb {
|
|
70
|
+
background-color: rgb(206, 206, 212);
|
|
71
|
+
border-radius: 10px;
|
|
72
|
+
}
|
|
@@ -8,13 +8,16 @@ import withPropsEditor from './with-props-editor';
|
|
|
8
8
|
import EditorHelp from './editor-help';
|
|
9
9
|
import { focusEditor } from '../../extension/core';
|
|
10
10
|
import { ScrollContext } from '../../hooks/use-scroll-context';
|
|
11
|
-
import '
|
|
11
|
+
import './style.css';
|
|
12
|
+
import useSeafileUtils from '../../hooks/use-insert-image';
|
|
12
13
|
export default function SlateEditor(_ref) {
|
|
13
14
|
let {
|
|
14
15
|
value,
|
|
15
16
|
editorApi,
|
|
16
17
|
onSave,
|
|
18
|
+
onContentChanged,
|
|
17
19
|
isSupportFormula,
|
|
20
|
+
isSupportInsertSeafileImage,
|
|
18
21
|
children
|
|
19
22
|
} = _ref;
|
|
20
23
|
const [slateValue, setSlateValue] = useState(value);
|
|
@@ -26,12 +29,17 @@ export default function SlateEditor(_ref) {
|
|
|
26
29
|
const eventProxy = useMemo(() => {
|
|
27
30
|
return new EventProxy(editor);
|
|
28
31
|
}, [editor]);
|
|
32
|
+
useSeafileUtils(editor);
|
|
29
33
|
const onChange = useCallback(value => {
|
|
30
34
|
setSlateValue(value);
|
|
31
|
-
|
|
35
|
+
const operations = editor.operations;
|
|
36
|
+
const modifyOps = operations.filter(o => o.type !== 'set_selection');
|
|
37
|
+
if (modifyOps.length > 0) {
|
|
38
|
+
onContentChanged && onContentChanged(value);
|
|
39
|
+
}
|
|
32
40
|
const eventBus = EventBus.getInstance();
|
|
33
41
|
eventBus.dispatch('change');
|
|
34
|
-
}, [
|
|
42
|
+
}, [editor.operations, onContentChanged]);
|
|
35
43
|
|
|
36
44
|
// useMount: focus editor
|
|
37
45
|
useEffect(() => {
|
|
@@ -54,12 +62,20 @@ export default function SlateEditor(_ref) {
|
|
|
54
62
|
};
|
|
55
63
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
56
64
|
}, []);
|
|
65
|
+
|
|
66
|
+
// willUnmount
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
return () => {
|
|
69
|
+
editor.selection = null;
|
|
70
|
+
};
|
|
71
|
+
});
|
|
57
72
|
return /*#__PURE__*/React.createElement("div", {
|
|
58
73
|
className: "sf-slate-editor-container"
|
|
59
74
|
}, /*#__PURE__*/React.createElement(Toolbar, {
|
|
60
75
|
editor: editor,
|
|
61
76
|
isRichEditor: true,
|
|
62
|
-
isSupportFormula: isSupportFormula
|
|
77
|
+
isSupportFormula: isSupportFormula,
|
|
78
|
+
isSupportInsertSeafileImage: isSupportInsertSeafileImage
|
|
63
79
|
}), /*#__PURE__*/React.createElement("div", {
|
|
64
80
|
className: "sf-slate-editor-content"
|
|
65
81
|
}, /*#__PURE__*/React.createElement(ScrollContext.Provider, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef } from 'react';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { baseEditor, renderElement, renderLeaf } from '../../extension';
|
|
3
3
|
import { Editable, Slate } from 'slate-react';
|
|
4
4
|
import Outline from '../../containers/outline';
|
|
@@ -7,15 +7,24 @@ import './style.css';
|
|
|
7
7
|
export default function SlateViewer(_ref) {
|
|
8
8
|
let {
|
|
9
9
|
value,
|
|
10
|
-
isShowOutline
|
|
10
|
+
isShowOutline,
|
|
11
|
+
scrollRef: externalScrollRef
|
|
11
12
|
} = _ref;
|
|
12
13
|
const scrollRef = useRef(null);
|
|
14
|
+
const containerScrollRef = externalScrollRef ? externalScrollRef : scrollRef;
|
|
15
|
+
|
|
16
|
+
// willUnmount
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
return () => {
|
|
19
|
+
baseEditor.selection = null;
|
|
20
|
+
};
|
|
21
|
+
});
|
|
13
22
|
return /*#__PURE__*/React.createElement(Slate, {
|
|
14
23
|
editor: baseEditor,
|
|
15
24
|
initialValue: value
|
|
16
25
|
}, /*#__PURE__*/React.createElement(ScrollContext.Provider, {
|
|
17
26
|
value: {
|
|
18
|
-
scrollRef
|
|
27
|
+
scrollRef: containerScrollRef
|
|
19
28
|
}
|
|
20
29
|
}, /*#__PURE__*/React.createElement("div", {
|
|
21
30
|
ref: scrollRef,
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
min-width: 0;
|
|
6
6
|
overflow: auto;
|
|
7
7
|
background: #f4f4f4;
|
|
8
|
-
border: 1px solid #ededed;
|
|
9
8
|
padding: 30px 0 15px;
|
|
10
9
|
}
|
|
11
10
|
|
|
@@ -20,7 +19,7 @@
|
|
|
20
19
|
|
|
21
20
|
.sf-slate-viewer-scroll-container .sf-slate-viewer-outline {
|
|
22
21
|
height: 80%;
|
|
23
|
-
overflow:
|
|
22
|
+
overflow-y: hidden;
|
|
24
23
|
padding-right: 1rem;
|
|
25
24
|
position: fixed;
|
|
26
25
|
right: 0;
|
|
@@ -28,6 +27,10 @@
|
|
|
28
27
|
width: 300px;
|
|
29
28
|
}
|
|
30
29
|
|
|
30
|
+
.sf-slate-viewer-scroll-container .sf-slate-viewer-outline:hover {
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
}
|
|
33
|
+
|
|
31
34
|
.sf-slate-viewer-scroll-container .article {
|
|
32
35
|
margin: 0 auto;
|
|
33
36
|
padding: 40px 60px;
|
|
@@ -37,21 +40,23 @@
|
|
|
37
40
|
background: #fff;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
@media (max-width:
|
|
43
|
+
@media (max-width: 991.98px) {
|
|
41
44
|
.sf-slate-viewer-article-container {
|
|
42
45
|
padding: 0 10px;
|
|
43
46
|
width: 100%;
|
|
44
47
|
margin: 0 !important;
|
|
45
48
|
}
|
|
49
|
+
|
|
50
|
+
.sf-slate-viewer-outline {
|
|
51
|
+
display: none !important;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
46
54
|
|
|
55
|
+
@media (max-width: 768px) {
|
|
47
56
|
.sf-slate-viewer-article-container .article {
|
|
48
57
|
margin: 0 !important;
|
|
49
58
|
padding: 20px !important;
|
|
50
59
|
}
|
|
51
|
-
|
|
52
|
-
.sf-slate-viewer-outline {
|
|
53
|
-
display: none !important;
|
|
54
|
-
}
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
|
|
@@ -4,13 +4,13 @@ export { _ELementTypes as ELementTypes };
|
|
|
4
4
|
export * from './menus-config';
|
|
5
5
|
export const HEADERS = [HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6];
|
|
6
6
|
export const HEADER_TITLE_MAP = {
|
|
7
|
-
[HEADER1]: '
|
|
8
|
-
[HEADER2]: '
|
|
9
|
-
[HEADER3]: '
|
|
10
|
-
[HEADER4]: '
|
|
11
|
-
[HEADER5]: '
|
|
12
|
-
[HEADER6]: '
|
|
13
|
-
[PARAGRAPH]: '
|
|
7
|
+
[HEADER1]: 'Header_one',
|
|
8
|
+
[HEADER2]: 'Header_two',
|
|
9
|
+
[HEADER3]: 'Header_three',
|
|
10
|
+
[HEADER4]: 'Header_four',
|
|
11
|
+
[HEADER5]: 'Header_five',
|
|
12
|
+
[HEADER6]: 'Header_six',
|
|
13
|
+
[PARAGRAPH]: 'Paragraph'
|
|
14
14
|
};
|
|
15
15
|
export const LIST_TYPE_ARRAY = ['unordered_list', 'ordered_list'];
|
|
16
16
|
export const INSERT_POSITION = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import slugid from 'slugid';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { PARAGRAPH } from '../../constants/element-types';
|
|
3
|
+
import { HEADER1, PARAGRAPH } from '../../constants/element-types';
|
|
4
4
|
export const match = (node, path, predicate) => {
|
|
5
5
|
if (!predicate) return true;
|
|
6
6
|
if (typeof predicate === 'object') {
|
|
@@ -35,6 +35,17 @@ export const generateEmptyElement = type => {
|
|
|
35
35
|
children: [generateDefaultText()]
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
|
+
export const generateHeaderElement = text => {
|
|
39
|
+
const headerText = {
|
|
40
|
+
id: slugid.nice(),
|
|
41
|
+
text: text
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
id: slugid.nice(),
|
|
45
|
+
type: HEADER1,
|
|
46
|
+
children: [headerText]
|
|
47
|
+
};
|
|
48
|
+
};
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* @param {String} type
|
|
@@ -11,12 +11,15 @@ export const isMenuDisabled = (editor, readonly) => {
|
|
|
11
11
|
if (isInCodeBlock(editor)) return true;
|
|
12
12
|
return false;
|
|
13
13
|
};
|
|
14
|
-
export const insertImage = (editor, url) => {
|
|
14
|
+
export const insertImage = (editor, url, title) => {
|
|
15
15
|
const imageNode = {
|
|
16
16
|
type: IMAGE,
|
|
17
17
|
id: slugid.nice(),
|
|
18
18
|
data: {
|
|
19
|
-
src: url
|
|
19
|
+
src: url,
|
|
20
|
+
...(title && {
|
|
21
|
+
title
|
|
22
|
+
})
|
|
20
23
|
},
|
|
21
24
|
children: [generateDefaultText()]
|
|
22
25
|
};
|
|
@@ -25,6 +28,29 @@ export const insertImage = (editor, url) => {
|
|
|
25
28
|
select: true
|
|
26
29
|
});
|
|
27
30
|
};
|
|
31
|
+
export const insertSeafileImage = _ref => {
|
|
32
|
+
let {
|
|
33
|
+
editor,
|
|
34
|
+
url,
|
|
35
|
+
title,
|
|
36
|
+
selection
|
|
37
|
+
} = _ref;
|
|
38
|
+
const imageNode = {
|
|
39
|
+
type: IMAGE,
|
|
40
|
+
id: slugid.nice(),
|
|
41
|
+
data: {
|
|
42
|
+
src: url,
|
|
43
|
+
...(title && {
|
|
44
|
+
title
|
|
45
|
+
})
|
|
46
|
+
},
|
|
47
|
+
children: [generateDefaultText()]
|
|
48
|
+
};
|
|
49
|
+
Transforms.insertNodes(editor, imageNode, {
|
|
50
|
+
at: selection,
|
|
51
|
+
select: true
|
|
52
|
+
});
|
|
53
|
+
};
|
|
28
54
|
export const updateImage = (editor, data) => {
|
|
29
55
|
Transforms.setNodes(editor, {
|
|
30
56
|
data
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import React, { useCallback, useState } from 'react';
|
|
1
|
+
import React, { Fragment, useCallback, useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { Label } from 'reactstrap';
|
|
4
3
|
import ImageMenuInsertInternetDialog from './image-menu-dialog';
|
|
4
|
+
import EventBus from '../../../../utils/event-bus';
|
|
5
|
+
import { EXTERNAL_EVENTS } from '../../../../constants/event-types';
|
|
5
6
|
import { insertImage } from '../helper';
|
|
6
7
|
import './style.css';
|
|
7
8
|
const ImageMenuPopover = _ref => {
|
|
8
9
|
let {
|
|
9
10
|
editor,
|
|
10
|
-
handelClosePopover
|
|
11
|
+
handelClosePopover,
|
|
12
|
+
isSupportInsertSeafileImage
|
|
11
13
|
} = _ref;
|
|
12
14
|
const [isShowInternetImageModal, setIsShowInternetImageModal] = useState(false);
|
|
13
15
|
const {
|
|
14
16
|
t
|
|
15
17
|
} = useTranslation();
|
|
16
|
-
const
|
|
18
|
+
const handleInsertNetworkImage = e => {
|
|
17
19
|
e.nativeEvent.stopImmediatePropagation();
|
|
18
20
|
e.stopPropagation();
|
|
19
21
|
setIsShowInternetImageModal(true);
|
|
@@ -39,14 +41,20 @@ const ImageMenuPopover = _ref => {
|
|
|
39
41
|
setIsShowInternetImageModal(false);
|
|
40
42
|
handelClosePopover();
|
|
41
43
|
}, [handelClosePopover]);
|
|
42
|
-
|
|
44
|
+
const handleInsertLibraryImage = e => {
|
|
45
|
+
e.nativeEvent.stopImmediatePropagation();
|
|
46
|
+
e.stopPropagation();
|
|
47
|
+
const eventBus = EventBus.getInstance();
|
|
48
|
+
eventBus.dispatch(EXTERNAL_EVENTS.ON_INSERT_IMAGE, editor.selection);
|
|
49
|
+
handelClosePopover();
|
|
50
|
+
};
|
|
51
|
+
return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
43
52
|
className: "image-popover"
|
|
44
53
|
}, /*#__PURE__*/React.createElement("div", {
|
|
45
54
|
className: "image-popover-item",
|
|
46
|
-
onClick:
|
|
47
|
-
}, t('Insert_network_image')), /*#__PURE__*/React.createElement(
|
|
55
|
+
onClick: handleInsertNetworkImage
|
|
56
|
+
}, t('Insert_network_image')), /*#__PURE__*/React.createElement("div", {
|
|
48
57
|
className: "image-popover-item",
|
|
49
|
-
for: "image-uploader",
|
|
50
58
|
onClick: handleClickFileInput
|
|
51
59
|
}, t('Upload_local_image')), /*#__PURE__*/React.createElement("input", {
|
|
52
60
|
onClick: handleClickFileInput,
|
|
@@ -55,7 +63,10 @@ const ImageMenuPopover = _ref => {
|
|
|
55
63
|
accept: "image/*",
|
|
56
64
|
className: "image-uploader",
|
|
57
65
|
id: "image-uploader"
|
|
58
|
-
}),
|
|
66
|
+
}), isSupportInsertSeafileImage && /*#__PURE__*/React.createElement("div", {
|
|
67
|
+
className: "image-popover-item",
|
|
68
|
+
onClick: handleInsertLibraryImage
|
|
69
|
+
}, t('Insert_library_image'))), isShowInternetImageModal && /*#__PURE__*/React.createElement(ImageMenuInsertInternetDialog, {
|
|
59
70
|
editor: editor,
|
|
60
71
|
onToggleImageDialog: onToggleImageDialog
|
|
61
72
|
}));
|
|
@@ -10,7 +10,8 @@ const ImageMenu = _ref => {
|
|
|
10
10
|
isRichEditor,
|
|
11
11
|
className,
|
|
12
12
|
readonly,
|
|
13
|
-
editor
|
|
13
|
+
editor,
|
|
14
|
+
isSupportInsertSeafileImage
|
|
14
15
|
} = _ref;
|
|
15
16
|
const [isShowImagePopover, setIsShowImagePopover] = useState(false);
|
|
16
17
|
useEffect(() => {
|
|
@@ -42,7 +43,8 @@ const ImageMenu = _ref => {
|
|
|
42
43
|
editor: editor,
|
|
43
44
|
setIsShowImagePopover: setIsShowImagePopover,
|
|
44
45
|
unregisterEventHandler: unregisterEventHandler,
|
|
45
|
-
handelClosePopover: handleChangePopoverDisplayed
|
|
46
|
+
handelClosePopover: handleChangePopoverDisplayed,
|
|
47
|
+
isSupportInsertSeafileImage: isSupportInsertSeafileImage
|
|
46
48
|
}));
|
|
47
49
|
};
|
|
48
50
|
export default ImageMenu;
|
|
@@ -92,12 +92,12 @@ export const insertLink = props => {
|
|
|
92
92
|
const isCollapsed = Range.isCollapsed(selection);
|
|
93
93
|
if (isCollapsed) {
|
|
94
94
|
// If selection is collapsed, we insert a space and then insert link node that help operation easier
|
|
95
|
-
editor.insertText('
|
|
95
|
+
editor.insertText('');
|
|
96
96
|
Editor.insertFragment(editor, [linkNode]);
|
|
97
97
|
// Using insertText directly causes the added Spaces to be added to the linked text, as in the issue above, so replaced by insertFragment
|
|
98
98
|
Editor.insertFragment(editor, [{
|
|
99
99
|
id: slugid.nice(),
|
|
100
|
-
text: '
|
|
100
|
+
text: ''
|
|
101
101
|
}]);
|
|
102
102
|
focusEditor(editor);
|
|
103
103
|
return;
|
|
@@ -120,6 +120,45 @@ export const insertLink = props => {
|
|
|
120
120
|
}
|
|
121
121
|
focusEditor(editor);
|
|
122
122
|
};
|
|
123
|
+
export const insertSeafileLink = _ref => {
|
|
124
|
+
let {
|
|
125
|
+
editor,
|
|
126
|
+
url,
|
|
127
|
+
title,
|
|
128
|
+
selection
|
|
129
|
+
} = _ref;
|
|
130
|
+
focusEditor(editor, selection);
|
|
131
|
+
const linkNode = generateLinkNode(url, title);
|
|
132
|
+
const isCollapsed = Range.isCollapsed(selection);
|
|
133
|
+
if (isCollapsed) {
|
|
134
|
+
// If selection is collapsed, we insert a space and then insert link node that help operation easier
|
|
135
|
+
editor.insertText('');
|
|
136
|
+
Editor.insertFragment(editor, [linkNode]);
|
|
137
|
+
// Using insertText directly causes the added Spaces to be added to the linked text, as in the issue above, so replaced by insertFragment
|
|
138
|
+
Editor.insertFragment(editor, [{
|
|
139
|
+
id: slugid.nice(),
|
|
140
|
+
text: ''
|
|
141
|
+
}]);
|
|
142
|
+
focusEditor(editor);
|
|
143
|
+
return;
|
|
144
|
+
} else {
|
|
145
|
+
const selectedText = Editor.string(editor, selection); // Selected text
|
|
146
|
+
if (selectedText !== title) {
|
|
147
|
+
// Replace the selected text with the link node if the selected text is different from the entered text
|
|
148
|
+
editor.deleteFragment();
|
|
149
|
+
Transforms.insertNodes(editor, linkNode);
|
|
150
|
+
} else {
|
|
151
|
+
// Wrap the selected text with the link node if the selected text is the same as the entered text
|
|
152
|
+
Transforms.wrapNodes(editor, linkNode, {
|
|
153
|
+
split: true,
|
|
154
|
+
at: selection
|
|
155
|
+
});
|
|
156
|
+
Transforms.collapse(editor, {
|
|
157
|
+
edge: 'end'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
123
162
|
export const getLinkInfo = editor => {
|
|
124
163
|
const isLinkNode = isLinkType(editor);
|
|
125
164
|
if (!isLinkNode) return null;
|
|
@@ -159,10 +198,10 @@ export const updateLink = (editor, newUrl, newText) => {
|
|
|
159
198
|
text: newText
|
|
160
199
|
});
|
|
161
200
|
};
|
|
162
|
-
export const upsertLinkText = (editor,
|
|
201
|
+
export const upsertLinkText = (editor, _ref2) => {
|
|
163
202
|
let {
|
|
164
203
|
text
|
|
165
|
-
} =
|
|
204
|
+
} = _ref2;
|
|
166
205
|
const newLink = getAboveNode(editor, {
|
|
167
206
|
match: {
|
|
168
207
|
type: ELementTypes.LINK
|
|
@@ -5,7 +5,7 @@ import { useReadOnly } from 'slate-react';
|
|
|
5
5
|
import LinkPopover from './link-popover';
|
|
6
6
|
import { getLinkInfo } from '../helper';
|
|
7
7
|
import EventBus from '../../../../utils/event-bus';
|
|
8
|
-
import { INTERNAL_EVENTS } from '../../../../constants/event-types';
|
|
8
|
+
import { EXTERNAL_EVENTS, INTERNAL_EVENTS } from '../../../../constants/event-types';
|
|
9
9
|
import './style.css';
|
|
10
10
|
const renderLink = (_ref, editor) => {
|
|
11
11
|
let {
|
|
@@ -30,11 +30,14 @@ const renderLink = (_ref, editor) => {
|
|
|
30
30
|
const unregisterClickEvent = useCallback(() => {
|
|
31
31
|
document.removeEventListener('click', onClosePopover);
|
|
32
32
|
}, [onClosePopover]);
|
|
33
|
-
const
|
|
34
|
-
if (isReadonly) return;
|
|
33
|
+
const onLinkClick = useCallback(e => {
|
|
35
34
|
e.stopPropagation();
|
|
36
|
-
// Only one popover can be open at the same time, close other popover and update new popover controller function.
|
|
37
35
|
const eventBus = EventBus.getInstance();
|
|
36
|
+
if (isReadonly) {
|
|
37
|
+
eventBus.dispatch(EXTERNAL_EVENTS.ON_LINK_CLICK, e);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Only one popover can be open at the same time, close other popover and update new popover controller function.
|
|
38
41
|
eventBus.dispatch(INTERNAL_EVENTS.ON_CLOSE_LINK_POPOVER);
|
|
39
42
|
eventBus.subscribe(INTERNAL_EVENTS.ON_CLOSE_LINK_POPOVER, () => setIsShowPopover(false));
|
|
40
43
|
const linkInfo = getLinkInfo(editor);
|
|
@@ -54,7 +57,8 @@ const renderLink = (_ref, editor) => {
|
|
|
54
57
|
registerClickEvent();
|
|
55
58
|
}, [editor, isReadonly, registerClickEvent]);
|
|
56
59
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", Object.assign({
|
|
57
|
-
onClick:
|
|
60
|
+
onClick: onLinkClick,
|
|
61
|
+
"data-url": element.url,
|
|
58
62
|
className: classNames('sf-virtual-link', {
|
|
59
63
|
selected: isShowPopover
|
|
60
64
|
})
|
|
@@ -26,7 +26,8 @@ const Toolbar = _ref => {
|
|
|
26
26
|
editor,
|
|
27
27
|
readonly = false,
|
|
28
28
|
isRichEditor = false,
|
|
29
|
-
isSupportFormula = false
|
|
29
|
+
isSupportFormula = false,
|
|
30
|
+
isSupportInsertSeafileImage = false
|
|
30
31
|
} = _ref;
|
|
31
32
|
useSelectionUpdate();
|
|
32
33
|
const [isShowArticleInfo, setIsShowArticleInfo] = useState(false);
|
|
@@ -65,7 +66,7 @@ const Toolbar = _ref => {
|
|
|
65
66
|
};
|
|
66
67
|
return /*#__PURE__*/React.createElement("div", {
|
|
67
68
|
className: "sf-slate-editor-toolbar"
|
|
68
|
-
}, /*#__PURE__*/React.createElement(MenuGroup, null), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(HeaderMenu, commonProps)), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(TextStyleMenu, Object.assign({}, commonProps, {
|
|
69
|
+
}, isRichEditor && /*#__PURE__*/React.createElement(MenuGroup, null), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(HeaderMenu, commonProps)), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(TextStyleMenu, Object.assign({}, commonProps, {
|
|
69
70
|
type: TEXT_STYLE_MAP.BOLD
|
|
70
71
|
})), /*#__PURE__*/React.createElement(TextStyleMenu, Object.assign({}, commonProps, {
|
|
71
72
|
type: TEXT_STYLE_MAP.ITALIC
|
|
@@ -75,7 +76,9 @@ const Toolbar = _ref => {
|
|
|
75
76
|
type: ORDERED_LIST
|
|
76
77
|
})), /*#__PURE__*/React.createElement(ListMenu, Object.assign({}, commonProps, {
|
|
77
78
|
type: UNORDERED_LIST
|
|
78
|
-
}))), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(CodeBlockMenu, commonProps), /*#__PURE__*/React.createElement(TableMenu, commonProps), /*#__PURE__*/React.createElement(ImageMenu,
|
|
79
|
+
}))), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(CodeBlockMenu, commonProps), /*#__PURE__*/React.createElement(TableMenu, commonProps), /*#__PURE__*/React.createElement(ImageMenu, Object.assign({}, commonProps, {
|
|
80
|
+
isSupportInsertSeafileImage: isSupportInsertSeafileImage
|
|
81
|
+
})), isSupportFormula && /*#__PURE__*/React.createElement(FormulaMenu, commonProps)), isShowSubTableMenu && /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(AlignmentDropDown, commonProps), /*#__PURE__*/React.createElement(ColumnOperationDropDownList, commonProps), /*#__PURE__*/React.createElement(RowOperationDropDownList, commonProps), /*#__PURE__*/React.createElement(RemoveTableMenu, commonProps)), /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(ClearFormatMenu, commonProps)), isRichEditor && /*#__PURE__*/React.createElement("div", {
|
|
79
82
|
className: "sf-slate-article-info-control",
|
|
80
83
|
onClick: updateArticleInfoState
|
|
81
84
|
}, /*#__PURE__*/React.createElement("span", {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import EventBus from '../utils/event-bus';
|
|
3
|
+
import { EXTERNAL_EVENTS } from '../constants/event-types';
|
|
4
|
+
import { insertSeafileImage } from '../extension/plugins/image/helper';
|
|
5
|
+
import { insertSeafileLink } from '../extension/plugins/link/helper';
|
|
6
|
+
const useSeafileUtils = editor => {
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const insertImage = _ref => {
|
|
9
|
+
let {
|
|
10
|
+
title,
|
|
11
|
+
url,
|
|
12
|
+
isImage,
|
|
13
|
+
selection
|
|
14
|
+
} = _ref;
|
|
15
|
+
if (isImage) {
|
|
16
|
+
insertSeafileImage({
|
|
17
|
+
editor,
|
|
18
|
+
title,
|
|
19
|
+
url,
|
|
20
|
+
selection
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
insertSeafileLink({
|
|
24
|
+
editor,
|
|
25
|
+
title,
|
|
26
|
+
url,
|
|
27
|
+
selection
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const eventBus = EventBus.getInstance();
|
|
32
|
+
const subscribe = eventBus.subscribe(EXTERNAL_EVENTS.INSERT_IMAGE, insertImage);
|
|
33
|
+
return subscribe;
|
|
34
|
+
}, [editor]);
|
|
35
|
+
};
|
|
36
|
+
export default useSeafileUtils;
|