@seafile/sdoc-editor 0.3.11 → 0.3.13
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/basic-sdk/comment/components/comment-item-wrapper.js +13 -5
- package/dist/basic-sdk/comment/components/comment-list.css +15 -0
- package/dist/basic-sdk/comment/components/elements-comment-count/element-comment-count.js +3 -11
- package/dist/basic-sdk/comment/components/global-comment/index.css +12 -0
- package/dist/basic-sdk/comment/components/global-comment/index.js +12 -3
- package/dist/basic-sdk/comment/utils/index.js +13 -0
- package/dist/basic-sdk/constants/index.js +2 -1
- package/dist/basic-sdk/extension/constants/element-type.js +1 -0
- package/dist/basic-sdk/extension/constants/index.js +21 -3
- package/dist/basic-sdk/extension/constants/menus-config.js +16 -1
- package/dist/basic-sdk/extension/core/utils/index.js +7 -4
- package/dist/basic-sdk/extension/plugins/blockquote/helpers.js +11 -3
- package/dist/basic-sdk/extension/plugins/blockquote/plugin.js +9 -0
- package/dist/basic-sdk/extension/plugins/callout/constant.js +47 -0
- package/dist/basic-sdk/extension/plugins/callout/helper.js +117 -0
- package/dist/basic-sdk/extension/plugins/callout/index.js +12 -0
- package/dist/basic-sdk/extension/plugins/callout/menu/index.css +16 -0
- package/dist/basic-sdk/extension/plugins/callout/menu/index.js +40 -0
- package/dist/basic-sdk/extension/plugins/callout/plugin.js +57 -0
- package/dist/basic-sdk/extension/plugins/callout/render-elem/color-selector.js +51 -0
- package/dist/basic-sdk/extension/plugins/callout/render-elem/index.css +59 -0
- package/dist/basic-sdk/extension/plugins/callout/render-elem/index.js +82 -0
- package/dist/basic-sdk/extension/plugins/code-block/helpers.js +2 -0
- package/dist/basic-sdk/extension/plugins/index.js +3 -2
- package/dist/basic-sdk/extension/plugins/paragraph/render-elem.js +5 -3
- package/dist/basic-sdk/extension/plugins/table/helpers.js +1 -0
- package/dist/basic-sdk/extension/plugins/table/popover/table-size-popover/index.css +2 -1
- package/dist/basic-sdk/extension/plugins/table/popover/table-size-popover/index.js +27 -9
- package/dist/basic-sdk/extension/plugins/table/popover/table-template/index.css +8 -4
- package/dist/basic-sdk/extension/render/custom-element.js +15 -4
- package/dist/basic-sdk/extension/render/helper.js +37 -0
- package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +4 -0
- package/dist/basic-sdk/extension/toolbar/side-toolbar/event.js +5 -1
- package/dist/basic-sdk/extension/toolbar/side-toolbar/helpers.js +13 -3
- package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +7 -1
- package/dist/basic-sdk/extension/toolbar/side-toolbar/insert-block-menu.js +13 -1
- package/package.json +1 -1
- package/public/locales/en/sdoc-editor.json +4 -1
- package/public/locales/zh_CN/sdoc-editor.json +4 -1
- package/public/media/sdoc-editor-font/iconfont.eot +0 -0
- package/public/media/sdoc-editor-font/iconfont.svg +10 -0
- package/public/media/sdoc-editor-font/iconfont.ttf +0 -0
- package/public/media/sdoc-editor-font/iconfont.woff +0 -0
- package/public/media/sdoc-editor-font/iconfont.woff2 +0 -0
- package/public/media/sdoc-editor-font.css +14 -7
|
@@ -2,6 +2,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
|
2
2
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import dayjs from 'dayjs';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
|
+
import { Node } from '@seafile/slate';
|
|
5
6
|
import context from '../../../context';
|
|
6
7
|
import { useCommentContext } from '../hooks/comment-hooks/use-comment-context';
|
|
7
8
|
import CommentItemContent from './comment-item-content';
|
|
@@ -10,9 +11,10 @@ import CommentEditor from './comment-editor';
|
|
|
10
11
|
import CommentItemResolvedReply from './comment-item-resolved-reply';
|
|
11
12
|
import CommentDeleteShadow from './comment-delete-shadow';
|
|
12
13
|
import { COMMENT_URL_CLASSNAME } from '../constants';
|
|
13
|
-
|
|
14
|
+
const CommentItemWrapper = _ref => {
|
|
14
15
|
let {
|
|
15
16
|
container,
|
|
17
|
+
element,
|
|
16
18
|
isActive,
|
|
17
19
|
comment,
|
|
18
20
|
onCommentClick,
|
|
@@ -227,14 +229,19 @@ export default function CommentItemWrapper(_ref) {
|
|
|
227
229
|
}, [isActive]);
|
|
228
230
|
const className = classNames('comment-ui-container', {
|
|
229
231
|
'active': isActive,
|
|
230
|
-
'sdoc-resolved': comment.resolved
|
|
232
|
+
'sdoc-resolved': comment.resolved,
|
|
233
|
+
'd-flex flex-column pt-0': element
|
|
231
234
|
});
|
|
232
235
|
const tip = comment.resolved ? 'Reopen_discussion' : 'Enter_a_reply';
|
|
233
236
|
return /*#__PURE__*/React.createElement("div", {
|
|
234
237
|
id: "comment-item-wrapper_".concat(comment.id),
|
|
235
238
|
className: className,
|
|
236
239
|
onClick: onItemClick
|
|
237
|
-
}, /*#__PURE__*/React.createElement("
|
|
240
|
+
}, element && /*#__PURE__*/React.createElement("div", {
|
|
241
|
+
className: "comment-item-selected-text"
|
|
242
|
+
}, /*#__PURE__*/React.createElement("i", {
|
|
243
|
+
className: "sdocfont sdoc-selected-text mr-2"
|
|
244
|
+
}), Node.string(element)), /*#__PURE__*/React.createElement("ul", {
|
|
238
245
|
ref: listRef,
|
|
239
246
|
className: "comment-item-list"
|
|
240
247
|
}, /*#__PURE__*/React.createElement(CommentItemContent, {
|
|
@@ -277,7 +284,8 @@ export default function CommentItemWrapper(_ref) {
|
|
|
277
284
|
deleteConfirm: _deleteComment,
|
|
278
285
|
setIsShowDeleteModal: setIsShowDeleteDialog
|
|
279
286
|
}));
|
|
280
|
-
}
|
|
287
|
+
};
|
|
281
288
|
CommentItemWrapper.defaultProps = {
|
|
282
289
|
container: 'sdoc-comment-list-container'
|
|
283
|
-
};
|
|
290
|
+
};
|
|
291
|
+
export default CommentItemWrapper;
|
|
@@ -18,6 +18,21 @@
|
|
|
18
18
|
box-shadow: 0 1px 3px rgba(0,0,0,.3), 0 4px 8px 3px rgba(0,0,0,.15);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
.sdoc-comment-list-container .comment-item-selected-text {
|
|
22
|
+
padding: 6px 16px;
|
|
23
|
+
width: 100%;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
text-overflow: ellipsis;
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
line-height: 20px;
|
|
29
|
+
border-radius: 8px 8px 0 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sdoc-comment-list-container .comment-item-selected-text .sdoc-selected-text {
|
|
33
|
+
font-size: 12px;
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
.sdoc-comment-list-container .comment-item-list {
|
|
22
37
|
max-height: 350px;
|
|
23
38
|
min-width: 280px;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { useSlateStatic } from '@seafile/slate-react';
|
|
3
|
-
import {
|
|
4
|
-
import { getNodeById, focusEditor, findPath } from '../../../extension/core';
|
|
3
|
+
import { getNodeById } from '../../../extension/core';
|
|
5
4
|
import { useScrollContext } from '../../../hooks/use-scroll-context';
|
|
6
5
|
import { getElementCommentCountTop } from '../../helper';
|
|
7
6
|
import { eventStopPropagation } from '../../../utils/mouse-event';
|
|
8
|
-
import {
|
|
7
|
+
import { focusToCommentElement } from '../../utils';
|
|
9
8
|
const ElementCommentCount = _ref => {
|
|
10
9
|
let {
|
|
11
10
|
elementId,
|
|
@@ -17,14 +16,7 @@ const ElementCommentCount = _ref => {
|
|
|
17
16
|
const [top, setTop] = useState(-9999);
|
|
18
17
|
const onClick = useCallback(event => {
|
|
19
18
|
eventStopPropagation(event);
|
|
20
|
-
|
|
21
|
-
const endOfFirstNode = Editor.end(editor, path);
|
|
22
|
-
const startOfFirstNode = Editor.start(editor, path);
|
|
23
|
-
const range = {
|
|
24
|
-
anchor: [ELEMENT_TYPE.LIST_ITEM, ELEMENT_TYPE.ORDERED_LIST, ELEMENT_TYPE.UNORDERED_LIST].includes(element.type) ? startOfFirstNode : endOfFirstNode,
|
|
25
|
-
focus: endOfFirstNode
|
|
26
|
-
};
|
|
27
|
-
focusEditor(editor, range);
|
|
19
|
+
focusToCommentElement(editor, element);
|
|
28
20
|
}, [editor, element]);
|
|
29
21
|
useEffect(() => {
|
|
30
22
|
var _scrollRef$current;
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
flex-direction: column;
|
|
51
51
|
min-height: 0;
|
|
52
52
|
position: relative;
|
|
53
|
+
background-color: #F5F5F5;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
.global-comments-popover .comments-panel-body__header {
|
|
@@ -128,3 +129,14 @@
|
|
|
128
129
|
left: 0;
|
|
129
130
|
margin-bottom: 0;
|
|
130
131
|
}
|
|
132
|
+
|
|
133
|
+
/* custom */
|
|
134
|
+
.global-comments-popover .sdoc-comment-list-container .comment-ui-container {
|
|
135
|
+
background-color: #FFF;
|
|
136
|
+
border: 1px solid #c7c7c7;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.global-comments-popover .sdoc-comment-list-container .comment-item-selected-text {
|
|
140
|
+
background-color: #F5F5F5;
|
|
141
|
+
border-bottom: 1px solid #c7c7c7;
|
|
142
|
+
}
|
|
@@ -2,6 +2,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
|
2
2
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import dayjs from 'dayjs';
|
|
4
4
|
import { useSlateStatic } from '@seafile/slate-react';
|
|
5
|
+
import { ReactEditor } from '@seafile/slate-react';
|
|
5
6
|
import { ElementPopover } from '../../../extension/commons';
|
|
6
7
|
import EventBus from '../../../utils/event-bus';
|
|
7
8
|
import useCommentList from '../../hooks/comment-hooks/use-comment-list';
|
|
@@ -14,6 +15,8 @@ import { useCommentContext } from '../../hooks/comment-hooks/use-comment-context
|
|
|
14
15
|
import GlobalCommentEditor from './global-comment-editor';
|
|
15
16
|
import { INTERNAL_EVENT } from '../../../constants';
|
|
16
17
|
import { getNodeById } from '../../../extension/core';
|
|
18
|
+
import { useScrollContext } from '../../../hooks/use-scroll-context';
|
|
19
|
+
import { focusToCommentElement } from '../../utils';
|
|
17
20
|
import './index.css';
|
|
18
21
|
const GlobalComment = _ref => {
|
|
19
22
|
let {
|
|
@@ -30,6 +33,7 @@ const GlobalComment = _ref => {
|
|
|
30
33
|
const [isShowCommentList, setShowCommentList] = useState(false);
|
|
31
34
|
const [showEditor, setShowEditor] = useState(false);
|
|
32
35
|
const editor = useSlateStatic();
|
|
36
|
+
const scrollRef = useScrollContext();
|
|
33
37
|
const toggle = useCallback(() => {
|
|
34
38
|
if (isShowCommentList) {
|
|
35
39
|
setActiveComment(null);
|
|
@@ -69,11 +73,15 @@ const GlobalComment = _ref => {
|
|
|
69
73
|
const hiddenComment = useCallback(() => {
|
|
70
74
|
setActiveComment(null);
|
|
71
75
|
}, []);
|
|
72
|
-
const onCommentClick = useCallback(comment => {
|
|
76
|
+
const onCommentClick = useCallback((comment, element) => {
|
|
73
77
|
if (activeComment && activeComment.id === comment.id) return;
|
|
74
78
|
setActiveComment(comment);
|
|
75
79
|
deleteUnseenNotifications && deleteUnseenNotifications(comment);
|
|
76
|
-
|
|
80
|
+
if (comment.detail.element_id === DOC_COMMENT_ELEMENT_ID) return;
|
|
81
|
+
const elementDom = ReactEditor.toDOMNode(editor, element, scrollRef);
|
|
82
|
+
scrollRef.current.scrollTop = elementDom.offsetTop - scrollRef.current.clientHeight / 3;
|
|
83
|
+
focusToCommentElement(editor, element);
|
|
84
|
+
}, [activeComment, deleteUnseenNotifications, editor, scrollRef]);
|
|
77
85
|
const {
|
|
78
86
|
dispatch
|
|
79
87
|
} = useCommentContext();
|
|
@@ -147,10 +155,11 @@ const GlobalComment = _ref => {
|
|
|
147
155
|
const isActive = activeComment && activeComment.id === comment.id;
|
|
148
156
|
return /*#__PURE__*/React.createElement(CommentItemWrapper, {
|
|
149
157
|
key: comment.id,
|
|
158
|
+
element: element,
|
|
150
159
|
container: "global-comment-list-container",
|
|
151
160
|
comment: comment,
|
|
152
161
|
isActive: isActive,
|
|
153
|
-
onCommentClick: onCommentClick,
|
|
162
|
+
onCommentClick: comment => onCommentClick(comment, element),
|
|
154
163
|
hiddenComment: hiddenComment,
|
|
155
164
|
updateScrollPosition: updateScrollPosition
|
|
156
165
|
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { Editor } from '@seafile/slate';
|
|
1
2
|
import { getEventTransfer } from '../../../utils';
|
|
2
3
|
import { COMMENT_URL_CLASSNAME } from '../constants';
|
|
3
4
|
import { KeyCodes } from '../../../constants';
|
|
4
5
|
import { createNotify, generatorNotificationKey } from './notification-utils';
|
|
6
|
+
import { focusEditor, findPath } from '../../extension/core';
|
|
7
|
+
import { ELEMENT_TYPE } from '../../extension/constants';
|
|
5
8
|
export const searchCollaborators = (collaborators, searchValue) => {
|
|
6
9
|
const validSearchValue = searchValue ? searchValue.trim().toLowerCase() : '';
|
|
7
10
|
const validCollaborators = Array.isArray(collaborators) && collaborators.length > 0 ? collaborators : [];
|
|
@@ -211,4 +214,14 @@ class CommentUtilities {
|
|
|
211
214
|
};
|
|
212
215
|
}
|
|
213
216
|
}
|
|
217
|
+
export const focusToCommentElement = (editor, element) => {
|
|
218
|
+
const path = findPath(editor, element);
|
|
219
|
+
const endOfFirstNode = Editor.end(editor, path);
|
|
220
|
+
const startOfFirstNode = Editor.start(editor, path);
|
|
221
|
+
const range = {
|
|
222
|
+
anchor: [ELEMENT_TYPE.LIST_ITEM, ELEMENT_TYPE.ORDERED_LIST, ELEMENT_TYPE.UNORDERED_LIST].includes(element.type) ? startOfFirstNode : endOfFirstNode,
|
|
223
|
+
focus: endOfFirstNode
|
|
224
|
+
};
|
|
225
|
+
focusEditor(editor, range);
|
|
226
|
+
};
|
|
214
227
|
export { CommentUtilities, createNotify, generatorNotificationKey };
|
|
@@ -12,7 +12,8 @@ export const INTERNAL_EVENT = {
|
|
|
12
12
|
ARTICLE_CLICK: 'hidden_comment',
|
|
13
13
|
UPDATE_TAG_VIEW: 'update_tag_view',
|
|
14
14
|
COMMENT_LIST_CLICK: 'comment_list_click',
|
|
15
|
-
UNSEEN_NOTIFICATIONS_COUNT: 'unseen_notifications_count'
|
|
15
|
+
UNSEEN_NOTIFICATIONS_COUNT: 'unseen_notifications_count',
|
|
16
|
+
DISPLAY_CALLOUT_COLOR_PICKER: 'display_callout_color_picker'
|
|
16
17
|
};
|
|
17
18
|
export const REVISION_DIFF_KEY = 'diff';
|
|
18
19
|
export const REVISION_DIFF_VALUE = '1';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// extension plugin
|
|
2
2
|
import * as ELEMENT_TYPE from './element-type';
|
|
3
|
-
import { BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES } from './element-type';
|
|
3
|
+
import { BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT } from './element-type';
|
|
4
4
|
export { DEFAULT_COLORS, STANDARD_COLORS, DEFAULT_RECENT_USED_LIST, DEFAULT_FONT_COLOR, RECENT_USED_HIGHLIGHT_COLORS_KEY, RECENT_USED_FONT_COLORS_KEY, RECENT_USED_TABLE_CELL_BACKGROUND_COLORS_KEY, DEFAULT_LAST_USED_FONT_COLOR, DEFAULT_LAST_USED_HIGHLIGHT_COLOR, DEFAULT_LAST_USED_TABLE_CELL_BACKGROUND_COLOR } from './color';
|
|
5
5
|
export { FONT_SIZE, DEFAULT_FONT, FONT, GOOGLE_FONT_CLASS, RECENT_USED_FONTS_KEY, SDOC_FONT_SIZE } from './font';
|
|
6
6
|
export { DIFF_TYPE, ADDED_STYLE, DELETED_STYLE } from './diff-view';
|
|
@@ -36,5 +36,23 @@ export const FILE_TYPE = {
|
|
|
36
36
|
[FILE_LINK]: 'file',
|
|
37
37
|
[SDOC_LINK]: 'sdoc'
|
|
38
38
|
};
|
|
39
|
-
export const SUPPORTED_SIDE_OPERATION_TYPE = [PARAGRAPH, SUBTITLE, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, CHECK_LIST_ITEM, CODE_BLOCK, TABLE, BLOCKQUOTE];
|
|
40
|
-
export
|
|
39
|
+
export const SUPPORTED_SIDE_OPERATION_TYPE = [PARAGRAPH, SUBTITLE, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, CHECK_LIST_ITEM, CODE_BLOCK, TABLE, BLOCKQUOTE, CALL_OUT];
|
|
40
|
+
export const MOUSE_ENTER_EVENT_DISABLED_MAP = {
|
|
41
|
+
[PARAGRAPH]: [CALL_OUT],
|
|
42
|
+
[TITLE]: [CALL_OUT],
|
|
43
|
+
[SUBTITLE]: [CALL_OUT],
|
|
44
|
+
[CHECK_LIST_ITEM]: [CALL_OUT],
|
|
45
|
+
[ORDERED_LIST]: [CALL_OUT],
|
|
46
|
+
[UNORDERED_LIST]: [CALL_OUT],
|
|
47
|
+
[UNORDERED_LIST]: [CALL_OUT],
|
|
48
|
+
[LIST_ITEM]: [CALL_OUT],
|
|
49
|
+
[BLOCKQUOTE]: [CALL_OUT],
|
|
50
|
+
[HEADER1]: [CALL_OUT],
|
|
51
|
+
[HEADER2]: [CALL_OUT],
|
|
52
|
+
[HEADER3]: [CALL_OUT],
|
|
53
|
+
[HEADER4]: [CALL_OUT],
|
|
54
|
+
[HEADER5]: [CALL_OUT],
|
|
55
|
+
[HEADER6]: [CALL_OUT],
|
|
56
|
+
[CALL_OUT]: [CALL_OUT]
|
|
57
|
+
};
|
|
58
|
+
export { ELEMENT_TYPE, BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RECENT_USED_HIGHLIGHT_COLORS_KEY, DEFAULT_LAST_USED_HIGHLIGHT_COLOR, RECENT_USED_FONT_COLORS_KEY, DEFAULT_FONT_COLOR, DEFAULT_LAST_USED_FONT_COLOR } from './color';
|
|
2
|
-
import { BLOCKQUOTE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, ORDERED_LIST, UNORDERED_LIST, CHECK_LIST_ITEM, CODE_BLOCK, LINK, IMAGE, TABLE, SDOC_LINK, FILE_LINK, PARAGRAPH } from './element-type';
|
|
2
|
+
import { BLOCKQUOTE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, ORDERED_LIST, UNORDERED_LIST, CHECK_LIST_ITEM, CODE_BLOCK, LINK, IMAGE, TABLE, SDOC_LINK, FILE_LINK, PARAGRAPH, CALL_OUT } from './element-type';
|
|
3
3
|
export const UNDO = 'undo';
|
|
4
4
|
export const REDO = 'redo';
|
|
5
5
|
export const CLEAR_FORMAT = 'clear_format';
|
|
@@ -184,6 +184,11 @@ export const MENUS_CONFIG_MAP = {
|
|
|
184
184
|
id: "sdoc_".concat(FILE_LINK),
|
|
185
185
|
iconClass: 'sdocfont sdoc-link-file',
|
|
186
186
|
text: 'Link_file'
|
|
187
|
+
},
|
|
188
|
+
[CALL_OUT]: {
|
|
189
|
+
id: "sdoc_".concat(CALL_OUT),
|
|
190
|
+
iconClass: 'sdocfont sdoc-callout',
|
|
191
|
+
text: 'Callout'
|
|
187
192
|
}
|
|
188
193
|
};
|
|
189
194
|
|
|
@@ -243,6 +248,11 @@ export const SIDE_TRANSFORM_MENUS_CONFIG = [{
|
|
|
243
248
|
iconClass: 'sdocfont sdoc-quote1',
|
|
244
249
|
type: BLOCKQUOTE,
|
|
245
250
|
text: 'Quote'
|
|
251
|
+
}, {
|
|
252
|
+
id: CALL_OUT,
|
|
253
|
+
iconClass: 'sdocfont sdoc-callout',
|
|
254
|
+
type: CALL_OUT,
|
|
255
|
+
text: 'Callout'
|
|
246
256
|
}];
|
|
247
257
|
|
|
248
258
|
// Side insert menu config
|
|
@@ -271,6 +281,11 @@ export const SIDE_INSERT_MENUS_CONFIG = {
|
|
|
271
281
|
type: CODE_BLOCK,
|
|
272
282
|
text: 'Code_block'
|
|
273
283
|
},
|
|
284
|
+
[CALL_OUT]: {
|
|
285
|
+
id: '',
|
|
286
|
+
iconClass: 'sdocfont sdoc-callout',
|
|
287
|
+
text: 'Callout'
|
|
288
|
+
},
|
|
274
289
|
[ORDERED_LIST]: {
|
|
275
290
|
id: '',
|
|
276
291
|
iconClass: 'sdocfont sdoc-list-ol',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import slugid from 'slugid';
|
|
3
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -21,12 +22,14 @@ export const generateDefaultText = () => {
|
|
|
21
22
|
text: ''
|
|
22
23
|
};
|
|
23
24
|
};
|
|
24
|
-
export const generateEmptyElement = type
|
|
25
|
-
|
|
25
|
+
export const generateEmptyElement = function (type) {
|
|
26
|
+
let props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
27
|
+
return _objectSpread(_objectSpread({
|
|
26
28
|
id: slugid.nice(),
|
|
27
|
-
type
|
|
29
|
+
type
|
|
30
|
+
}, props), {}, {
|
|
28
31
|
children: [generateDefaultText()]
|
|
29
|
-
};
|
|
32
|
+
});
|
|
30
33
|
};
|
|
31
34
|
export function Placeholder(props) {
|
|
32
35
|
const {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import { Editor, Transforms, Element } from '@seafile/slate';
|
|
3
3
|
import slugid from 'slugid';
|
|
4
|
-
import { BLOCKQUOTE, CHECK_LIST_ITEM, IMAGE, ORDERED_LIST, PARAGRAPH, UNORDERED_LIST, CODE_BLOCK, TABLE } from '../../constants';
|
|
4
|
+
import { BLOCKQUOTE, CHECK_LIST_ITEM, IMAGE, ORDERED_LIST, PARAGRAPH, UNORDERED_LIST, CODE_BLOCK, TABLE, CALL_OUT } from '../../constants';
|
|
5
5
|
import { focusEditor, getNodeType } from '../../core';
|
|
6
6
|
export const isMenuDisabled = (editor, readonly) => {
|
|
7
7
|
if (readonly) return true;
|
|
@@ -40,6 +40,8 @@ export const getBlockQuoteType = editor => {
|
|
|
40
40
|
const [n] = match;
|
|
41
41
|
return getNodeType(n);
|
|
42
42
|
};
|
|
43
|
+
|
|
44
|
+
// If cursor is in callout, insert block quote in callout, otherwise wrap block quote directly
|
|
43
45
|
export const setBlockQuoteType = (editor, active) => {
|
|
44
46
|
if (!active) {
|
|
45
47
|
const blockquoteNode = {
|
|
@@ -48,12 +50,18 @@ export const setBlockQuoteType = (editor, active) => {
|
|
|
48
50
|
};
|
|
49
51
|
Transforms.wrapNodes(editor, blockquoteNode, {
|
|
50
52
|
mode: 'highest',
|
|
51
|
-
match: n =>
|
|
53
|
+
match: n => {
|
|
54
|
+
if (n.type === CALL_OUT) return false;
|
|
55
|
+
return Element.isElement(n) && Editor.isBlock(editor, n);
|
|
56
|
+
}
|
|
52
57
|
});
|
|
53
58
|
} else {
|
|
54
59
|
Transforms.unwrapNodes(editor, {
|
|
55
60
|
mode: 'highest',
|
|
56
|
-
match: n =>
|
|
61
|
+
match: n => {
|
|
62
|
+
if (n.type === CALL_OUT) return false;
|
|
63
|
+
return Element.isElement(n) && Editor.isBlock(editor, n);
|
|
64
|
+
}
|
|
57
65
|
});
|
|
58
66
|
}
|
|
59
67
|
focusEditor(editor);
|
|
@@ -4,6 +4,7 @@ import { ReactEditor } from '@seafile/slate-react';
|
|
|
4
4
|
import { generateEmptyElement, getSelectedNodeByType, isSelectionAtBlockStart } from '../../core';
|
|
5
5
|
import { BLOCKQUOTE, PARAGRAPH, CODE_BLOCK, TABLE } from '../../constants';
|
|
6
6
|
import { setBlockQuoteType, getFormattedElements, getFormattedRestElements } from './helpers';
|
|
7
|
+
import { getCalloutEntry, insertElementAtNewLineInCallout } from '../callout/helper';
|
|
7
8
|
const withBlockquote = editor => {
|
|
8
9
|
const {
|
|
9
10
|
insertBreak,
|
|
@@ -30,6 +31,14 @@ const withBlockquote = editor => {
|
|
|
30
31
|
const str = Node.string(quoteElem);
|
|
31
32
|
// The last of the blockquote text is \n;
|
|
32
33
|
if (str && str.slice(-1) === '\n') {
|
|
34
|
+
const calloutEntry = getCalloutEntry(editor);
|
|
35
|
+
if (calloutEntry) {
|
|
36
|
+
// If the cursor is at the end of the callout, insert a paragraph inside the callout
|
|
37
|
+
// Remove the last \n
|
|
38
|
+
editor.deleteBackward('character');
|
|
39
|
+
insertElementAtNewLineInCallout(editor, PARAGRAPH, quotePath);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
33
42
|
// Step 1: Remove the last \n
|
|
34
43
|
editor.deleteBackward('character');
|
|
35
44
|
// Step 2: Insert a paragraph
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BLOCKQUOTE, CALL_OUT, CHECK_LIST_ITEM, IMAGE, LINK, ORDERED_LIST, PARAGRAPH, SDOC_LINK, SUBTITLE, TITLE, UNORDERED_LIST } from '../../constants/element-type';
|
|
2
|
+
import { HEADERS, LIST_ITEM_CORRELATION_TYPE } from '../../constants';
|
|
3
|
+
|
|
4
|
+
// key as container fill color
|
|
5
|
+
export const CALLOUT_COLOR_MAP = {
|
|
6
|
+
'#f1f3f6': {
|
|
7
|
+
border_color: '#d9dbe0',
|
|
8
|
+
background_color: '#f1f3f6'
|
|
9
|
+
},
|
|
10
|
+
'#e1e9fe': {
|
|
11
|
+
border_color: '#cbdeff',
|
|
12
|
+
background_color: '#e1e9fe'
|
|
13
|
+
},
|
|
14
|
+
'#def0ff': {
|
|
15
|
+
border_color: '#c7ecff',
|
|
16
|
+
background_color: '#def0ff'
|
|
17
|
+
},
|
|
18
|
+
'#e7f9ee': {
|
|
19
|
+
border_color: '#a5dfbf',
|
|
20
|
+
background_color: '#e7f9ee'
|
|
21
|
+
},
|
|
22
|
+
'#eaf7d6': {
|
|
23
|
+
border_color: '#c3e788',
|
|
24
|
+
background_color: '#eaf7d6'
|
|
25
|
+
},
|
|
26
|
+
'#fef7e0': {
|
|
27
|
+
border_color: '#faecb3',
|
|
28
|
+
background_color: '#fef7e0'
|
|
29
|
+
},
|
|
30
|
+
'#fff1e8': {
|
|
31
|
+
border_color: '#ffe1cd',
|
|
32
|
+
background_color: '#fff1e8'
|
|
33
|
+
},
|
|
34
|
+
'#ffe6e3': {
|
|
35
|
+
border_color: '#ffc6c4',
|
|
36
|
+
background_color: '#ffe6e3'
|
|
37
|
+
},
|
|
38
|
+
'#ffe9f2': {
|
|
39
|
+
border_color: '#ffd0e6',
|
|
40
|
+
background_color: '#ffe9f2'
|
|
41
|
+
},
|
|
42
|
+
'#fde8ff': {
|
|
43
|
+
border_color: '#f0c1ff',
|
|
44
|
+
background_color: '#fde8ff'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export const CALLOUT_ALLOWED_INSIDE_TYPES = [CALL_OUT, ORDERED_LIST, UNORDERED_LIST, PARAGRAPH, TITLE, SUBTITLE, BLOCKQUOTE, ...HEADERS, ...LIST_ITEM_CORRELATION_TYPE, CHECK_LIST_ITEM, IMAGE, LINK, SDOC_LINK];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Editor, Path, Transforms } from '@seafile/slate';
|
|
2
|
+
import { CALL_OUT } from '../../constants/element-type';
|
|
3
|
+
import { findPath, focusEditor, generateEmptyElement, getSelectedElems, isRangeAcrossBlocks } from '../../core';
|
|
4
|
+
import { CALLOUT_ALLOWED_INSIDE_TYPES, CALLOUT_COLOR_MAP } from './constant';
|
|
5
|
+
export const isMenuActive = editor => {
|
|
6
|
+
const {
|
|
7
|
+
selection
|
|
8
|
+
} = editor;
|
|
9
|
+
if (!selection) return false;
|
|
10
|
+
const isInsideCallout = !!getCalloutEntry(editor);
|
|
11
|
+
if (isInsideCallout) return true;
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
14
|
+
export const isMenuDisabled = (editor, readonly) => {
|
|
15
|
+
if (readonly) return true;
|
|
16
|
+
const {
|
|
17
|
+
selection
|
|
18
|
+
} = editor;
|
|
19
|
+
if (!selection) return true;
|
|
20
|
+
const selectedElements = getSelectedElems(editor);
|
|
21
|
+
const isRangeAcrossBlock = isRangeAcrossBlocks(editor);
|
|
22
|
+
// If selected multiple block element contains callout, disable callout menu
|
|
23
|
+
const isAllSelectedElementsInAllowTypes = selectedElements.length && selectedElements.every(element => {
|
|
24
|
+
if (element.type === CALL_OUT && isRangeAcrossBlock) return false;
|
|
25
|
+
return CALLOUT_ALLOWED_INSIDE_TYPES.includes(element.type);
|
|
26
|
+
});
|
|
27
|
+
if (isAllSelectedElementsInAllowTypes) return false;
|
|
28
|
+
return true;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {keyof CALLOUT_COLOR_MAP} [background_color] fill color
|
|
33
|
+
*/
|
|
34
|
+
export const generateCallout = function () {
|
|
35
|
+
let background_color = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(CALLOUT_COLOR_MAP)[0];
|
|
36
|
+
const props = {
|
|
37
|
+
style: {
|
|
38
|
+
background_color
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const callout = generateEmptyElement(CALL_OUT, props);
|
|
42
|
+
callout.children = [];
|
|
43
|
+
return callout;
|
|
44
|
+
};
|
|
45
|
+
export const wrapCallout = editor => {
|
|
46
|
+
const {
|
|
47
|
+
selection
|
|
48
|
+
} = editor;
|
|
49
|
+
if (!selection) return;
|
|
50
|
+
const callout = generateCallout();
|
|
51
|
+
Transforms.wrapNodes(editor, callout, {
|
|
52
|
+
mode: 'highest'
|
|
53
|
+
});
|
|
54
|
+
focusEditor(editor);
|
|
55
|
+
};
|
|
56
|
+
export const unwrapCallout = editor => {
|
|
57
|
+
const {
|
|
58
|
+
selection
|
|
59
|
+
} = editor;
|
|
60
|
+
if (!selection) return;
|
|
61
|
+
const aboveNodeEntry = Editor.above(editor, {
|
|
62
|
+
match: n => n.type === CALL_OUT
|
|
63
|
+
});
|
|
64
|
+
if (!aboveNodeEntry) return;
|
|
65
|
+
const [, calloutPath] = aboveNodeEntry;
|
|
66
|
+
Transforms.unwrapNodes(editor, {
|
|
67
|
+
at: calloutPath,
|
|
68
|
+
match: n => n.type === CALL_OUT
|
|
69
|
+
});
|
|
70
|
+
const point = Editor.point(editor, editor.selection);
|
|
71
|
+
focusEditor(editor, point);
|
|
72
|
+
};
|
|
73
|
+
export const changeFillBackgroundColor = (editor, background_color) => {
|
|
74
|
+
Transforms.setNodes(editor, {
|
|
75
|
+
style: {
|
|
76
|
+
background_color
|
|
77
|
+
}
|
|
78
|
+
}, {
|
|
79
|
+
match: n => n.type === CALL_OUT
|
|
80
|
+
});
|
|
81
|
+
Transforms.select(editor, Editor.start(editor, editor.selection));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Check is cursor in callout
|
|
85
|
+
export const getCalloutEntry = function (editor) {
|
|
86
|
+
let at = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : editor.selection;
|
|
87
|
+
const aboveNodeEntry = Editor.above(editor, {
|
|
88
|
+
match: n => n.type === CALL_OUT,
|
|
89
|
+
mode: 'highest',
|
|
90
|
+
at
|
|
91
|
+
});
|
|
92
|
+
return aboveNodeEntry;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Use in render-elem, to judge whether to display placeholder
|
|
96
|
+
export const isFocusOnElement = (editor, element) => {
|
|
97
|
+
var _editor$selection, _editor$selection$anc;
|
|
98
|
+
if (!editor.selection || !element) return false;
|
|
99
|
+
const elementPath = findPath(editor, element);
|
|
100
|
+
const isSelectOnElement = Path.isAncestor(elementPath, editor === null || editor === void 0 ? void 0 : (_editor$selection = editor.selection) === null || _editor$selection === void 0 ? void 0 : (_editor$selection$anc = _editor$selection.anchor) === null || _editor$selection$anc === void 0 ? void 0 : _editor$selection$anc.path);
|
|
101
|
+
return isSelectOnElement;
|
|
102
|
+
};
|
|
103
|
+
export const isCalloutContentEmpty = (editor, calloutEntry) => {
|
|
104
|
+
const [, calloutPath] = calloutEntry;
|
|
105
|
+
const contentString = Editor.string(editor, calloutPath);
|
|
106
|
+
return contentString.length === 0;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Insert a new element at new line in callout after current path
|
|
110
|
+
export const insertElementAtNewLineInCallout = (editor, type, currentPath) => {
|
|
111
|
+
const element = generateEmptyElement(type);
|
|
112
|
+
const insertPath = Path.next(currentPath);
|
|
113
|
+
Transforms.insertNodes(editor, element, {
|
|
114
|
+
at: insertPath
|
|
115
|
+
});
|
|
116
|
+
Transforms.select(editor, insertPath);
|
|
117
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CALL_OUT } from '../../constants';
|
|
2
|
+
import CalloutMenu from './menu';
|
|
3
|
+
import withCallout from './plugin';
|
|
4
|
+
import renderCallout from './render-elem';
|
|
5
|
+
const CalloutPlugin = {
|
|
6
|
+
type: CALL_OUT,
|
|
7
|
+
nodeType: 'element',
|
|
8
|
+
editorMenus: [CalloutMenu],
|
|
9
|
+
editorPlugin: withCallout,
|
|
10
|
+
renderElements: [renderCallout]
|
|
11
|
+
};
|
|
12
|
+
export default CalloutPlugin;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.sdoc-callout-paste-unsupportable-type-alert {
|
|
2
|
+
display: none;
|
|
3
|
+
position: absolute;
|
|
4
|
+
top: 140px;
|
|
5
|
+
z-index: 1001;
|
|
6
|
+
left: 50%;
|
|
7
|
+
margin: auto;
|
|
8
|
+
transform: translateX(-50%);
|
|
9
|
+
transition: all 0.3s;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.sdoc-callout-paste-unsupportable-type-alert.display {
|
|
14
|
+
display: block;
|
|
15
|
+
transition: all 0.3s;
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { useCallback, useEffect } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { MenuItem } from '../../../commons';
|
|
4
|
+
import { MENUS_CONFIG_MAP } from '../../../constants';
|
|
5
|
+
import { CALL_OUT } from '../../../constants/element-type';
|
|
6
|
+
import { isMenuActive, isMenuDisabled, unwrapCallout, wrapCallout } from '../helper';
|
|
7
|
+
import { INTERNAL_EVENT } from '../../../../constants';
|
|
8
|
+
import toaster from '../../../../../components/toast';
|
|
9
|
+
import EventBus from '../../../../utils/event-bus';
|
|
10
|
+
const menuConfig = MENUS_CONFIG_MAP[CALL_OUT];
|
|
11
|
+
const CalloutMenu = _ref => {
|
|
12
|
+
let {
|
|
13
|
+
editor,
|
|
14
|
+
isRichEditor,
|
|
15
|
+
className,
|
|
16
|
+
readonly
|
|
17
|
+
} = _ref;
|
|
18
|
+
const {
|
|
19
|
+
t
|
|
20
|
+
} = useTranslation();
|
|
21
|
+
const handleDisplayAlert = useCallback(type => {
|
|
22
|
+
toaster.warning("".concat(t('The_current_location_does_not_support_pasting')).concat(t(type && type.at(0).toUpperCase() + type.slice(1))));
|
|
23
|
+
}, [t]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const eventBus = EventBus.getInstance();
|
|
26
|
+
const unsubscribe = eventBus.subscribe(INTERNAL_EVENT.DISPLAY_CALLOUT_UNSUPPORT_ALERT, handleDisplayAlert);
|
|
27
|
+
return unsubscribe;
|
|
28
|
+
}, [handleDisplayAlert, t]);
|
|
29
|
+
const handleClick = useCallback(e => {
|
|
30
|
+
isMenuActive(editor) ? unwrapCallout(editor) : wrapCallout(editor);
|
|
31
|
+
}, [editor]);
|
|
32
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MenuItem, Object.assign({
|
|
33
|
+
isRichEditor: isRichEditor,
|
|
34
|
+
className: className,
|
|
35
|
+
disabled: isMenuDisabled(editor, readonly),
|
|
36
|
+
isActive: isMenuActive(editor),
|
|
37
|
+
onMouseDown: handleClick
|
|
38
|
+
}, menuConfig)));
|
|
39
|
+
};
|
|
40
|
+
export default CalloutMenu;
|