@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.
Files changed (32) hide show
  1. package/dist/constants/event-types.js +4 -1
  2. package/dist/editors/plain-markdown-editor/index.js +1 -1
  3. package/dist/editors/simple-slate-editor /index.js +16 -4
  4. package/dist/editors/simple-slate-editor /style.css +72 -0
  5. package/dist/editors/slate-editor/index.js +20 -4
  6. package/dist/editors/slate-viewer/index.js +12 -3
  7. package/dist/editors/slate-viewer/style.css +12 -7
  8. package/dist/extension/constants/index.js +7 -7
  9. package/dist/extension/core/utils/index.js +12 -1
  10. package/dist/extension/plugins/image/helper.js +28 -2
  11. package/dist/extension/plugins/image/menu/image-menu-popover.js +20 -9
  12. package/dist/extension/plugins/image/menu/index.js +4 -2
  13. package/dist/extension/plugins/image/menu/style.css +2 -0
  14. package/dist/extension/plugins/link/helper.js +43 -4
  15. package/dist/extension/plugins/link/render-elem/index.js +9 -5
  16. package/dist/extension/toolbar/header-toolbar/index.js +6 -3
  17. package/dist/hooks/use-insert-image.js +36 -0
  18. package/dist/pages/markdown-editor.js +7 -5
  19. package/dist/pages/markdown-view.js +3 -1
  20. package/dist/pages/rich-markdown-editor.js +15 -6
  21. package/dist/pages/simple-editor.js +7 -5
  22. package/dist/slate-convert/md-to-slate/transform.js +14 -3
  23. package/dist/utils/event-handler.js +4 -0
  24. package/package.json +1 -1
  25. package/public/locales/cs/seafile-editor.json +170 -116
  26. package/public/locales/de/seafile-editor.json +184 -130
  27. package/public/locales/es/seafile-editor.json +171 -117
  28. package/public/locales/fr/seafile-editor.json +174 -120
  29. package/public/locales/it/seafile-editor.json +171 -117
  30. package/public/locales/ru/seafile-editor.json +171 -117
  31. package/public/locales/zh-CN/seafile-editor.json +4 -4
  32. /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.onSave && this.props.onSave(newCode);
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 '../../assets/css/slate-editor.css';
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
- onSave && onSave(value);
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
- }, [onSave]);
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 '../../assets/css/slate-editor.css';
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
- onSave && onSave(value);
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
- }, [onSave]);
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: auto;
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: 768px) {
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]: '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'
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 handleInsertNextworkImage = e => {
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
- return /*#__PURE__*/React.createElement("div", {
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: handleInsertNextworkImage
47
- }, t('Insert_network_image')), /*#__PURE__*/React.createElement(Label, {
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
- }), isShowInternetImageModal && /*#__PURE__*/React.createElement(ImageMenuInsertInternetDialog, {
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;
@@ -25,10 +25,12 @@
25
25
  font-size: 14px;
26
26
  line-height: 1;
27
27
  white-space: nowrap;
28
+ cursor: pointer;
28
29
  }
29
30
 
30
31
  .image-popover .image-popover-item:hover {
31
32
  background-color: #4d9ef8;
33
+ color: #fff;
32
34
  }
33
35
 
34
36
  .image-popover .image-uploader {
@@ -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, _ref) => {
201
+ export const upsertLinkText = (editor, _ref2) => {
163
202
  let {
164
203
  text
165
- } = _ref;
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 onOpenPopover = useCallback(e => {
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: onOpenPopover,
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, commonProps), 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
+ }))), /*#__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;