@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.
Files changed (45) hide show
  1. package/dist/basic-sdk/comment/components/comment-item-wrapper.js +13 -5
  2. package/dist/basic-sdk/comment/components/comment-list.css +15 -0
  3. package/dist/basic-sdk/comment/components/elements-comment-count/element-comment-count.js +3 -11
  4. package/dist/basic-sdk/comment/components/global-comment/index.css +12 -0
  5. package/dist/basic-sdk/comment/components/global-comment/index.js +12 -3
  6. package/dist/basic-sdk/comment/utils/index.js +13 -0
  7. package/dist/basic-sdk/constants/index.js +2 -1
  8. package/dist/basic-sdk/extension/constants/element-type.js +1 -0
  9. package/dist/basic-sdk/extension/constants/index.js +21 -3
  10. package/dist/basic-sdk/extension/constants/menus-config.js +16 -1
  11. package/dist/basic-sdk/extension/core/utils/index.js +7 -4
  12. package/dist/basic-sdk/extension/plugins/blockquote/helpers.js +11 -3
  13. package/dist/basic-sdk/extension/plugins/blockquote/plugin.js +9 -0
  14. package/dist/basic-sdk/extension/plugins/callout/constant.js +47 -0
  15. package/dist/basic-sdk/extension/plugins/callout/helper.js +117 -0
  16. package/dist/basic-sdk/extension/plugins/callout/index.js +12 -0
  17. package/dist/basic-sdk/extension/plugins/callout/menu/index.css +16 -0
  18. package/dist/basic-sdk/extension/plugins/callout/menu/index.js +40 -0
  19. package/dist/basic-sdk/extension/plugins/callout/plugin.js +57 -0
  20. package/dist/basic-sdk/extension/plugins/callout/render-elem/color-selector.js +51 -0
  21. package/dist/basic-sdk/extension/plugins/callout/render-elem/index.css +59 -0
  22. package/dist/basic-sdk/extension/plugins/callout/render-elem/index.js +82 -0
  23. package/dist/basic-sdk/extension/plugins/code-block/helpers.js +2 -0
  24. package/dist/basic-sdk/extension/plugins/index.js +3 -2
  25. package/dist/basic-sdk/extension/plugins/paragraph/render-elem.js +5 -3
  26. package/dist/basic-sdk/extension/plugins/table/helpers.js +1 -0
  27. package/dist/basic-sdk/extension/plugins/table/popover/table-size-popover/index.css +2 -1
  28. package/dist/basic-sdk/extension/plugins/table/popover/table-size-popover/index.js +27 -9
  29. package/dist/basic-sdk/extension/plugins/table/popover/table-template/index.css +8 -4
  30. package/dist/basic-sdk/extension/render/custom-element.js +15 -4
  31. package/dist/basic-sdk/extension/render/helper.js +37 -0
  32. package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +4 -0
  33. package/dist/basic-sdk/extension/toolbar/side-toolbar/event.js +5 -1
  34. package/dist/basic-sdk/extension/toolbar/side-toolbar/helpers.js +13 -3
  35. package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +7 -1
  36. package/dist/basic-sdk/extension/toolbar/side-toolbar/insert-block-menu.js +13 -1
  37. package/package.json +1 -1
  38. package/public/locales/en/sdoc-editor.json +4 -1
  39. package/public/locales/zh_CN/sdoc-editor.json +4 -1
  40. package/public/media/sdoc-editor-font/iconfont.eot +0 -0
  41. package/public/media/sdoc-editor-font/iconfont.svg +10 -0
  42. package/public/media/sdoc-editor-font/iconfont.ttf +0 -0
  43. package/public/media/sdoc-editor-font/iconfont.woff +0 -0
  44. package/public/media/sdoc-editor-font/iconfont.woff2 +0 -0
  45. 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
- export default function CommentItemWrapper(_ref) {
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("ul", {
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 { Editor } from '@seafile/slate';
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 { ELEMENT_TYPE } from '../../../extension/constants';
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
- const path = findPath(editor, element);
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
- }, [activeComment, deleteUnseenNotifications]);
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';
@@ -22,6 +22,7 @@ export const LINK = 'link';
22
22
  export const SDOC_LINK = 'sdoc_link';
23
23
  export const FILE_LINK = 'file_link';
24
24
  export const IMAGE = 'image';
25
+ export const CALL_OUT = 'callout';
25
26
 
26
27
  // font
27
28
  export const FONT_SIZE = 'font-size';
@@ -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 { 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 };
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
- return {
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 => Element.isElement(n) && Editor.isBlock(editor, 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 => Element.isElement(n) && Editor.isBlock(editor, 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;