@seafile/sdoc-editor 0.5.65 → 0.5.67-beta

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 (51) hide show
  1. package/dist/assets/css/plugin-editor.css +3 -0
  2. package/dist/basic-sdk/extension/commons/index.js +2 -1
  3. package/dist/basic-sdk/extension/commons/select/_option.js +32 -0
  4. package/dist/basic-sdk/extension/commons/select/field-setting.js +101 -0
  5. package/dist/basic-sdk/extension/commons/select/index.js +135 -0
  6. package/dist/basic-sdk/extension/commons/select/menu/index.js +4 -0
  7. package/dist/basic-sdk/extension/commons/select/menu/item.js +32 -0
  8. package/dist/basic-sdk/extension/commons/select/menu/menu.js +27 -0
  9. package/dist/basic-sdk/extension/commons/select/menu/style.css +43 -0
  10. package/dist/basic-sdk/extension/commons/select/style.css +149 -0
  11. package/dist/basic-sdk/extension/constants/element-type.js +4 -0
  12. package/dist/basic-sdk/extension/constants/index.js +2 -2
  13. package/dist/basic-sdk/extension/constants/menus-config.js +6 -1
  14. package/dist/basic-sdk/extension/plugins/column/constants/cell-types.js +29 -0
  15. package/dist/basic-sdk/extension/plugins/column/constants/column.js +28 -0
  16. package/dist/basic-sdk/extension/plugins/column/helpers.js +62 -0
  17. package/dist/basic-sdk/extension/plugins/column/index.js +12 -0
  18. package/dist/basic-sdk/extension/plugins/column/menu/index.js +28 -0
  19. package/dist/basic-sdk/extension/plugins/column/model.js +14 -0
  20. package/dist/basic-sdk/extension/plugins/column/plugin.js +24 -0
  21. package/dist/basic-sdk/extension/plugins/column/render-elem.js +111 -0
  22. package/dist/basic-sdk/extension/plugins/font/helpers.js +1 -0
  23. package/dist/basic-sdk/extension/plugins/group/index.js +7 -0
  24. package/dist/basic-sdk/extension/plugins/group/render-elem.js +17 -0
  25. package/dist/basic-sdk/extension/plugins/index.js +4 -2
  26. package/dist/basic-sdk/extension/plugins/text-align/helpers.js +1 -0
  27. package/dist/basic-sdk/extension/render/custom-element.js +12 -1
  28. package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +7 -2
  29. package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/index.js +19 -4
  30. package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +16 -1
  31. package/dist/basic-sdk/extension/toolbar/side-toolbar/side-menu.js +10 -3
  32. package/dist/basic-sdk/hooks/use-force-update.js +9 -0
  33. package/dist/basic-sdk/utils/rebase.js +93 -14
  34. package/dist/components/doc-operations/revision-operations/index.js +1 -0
  35. package/dist/index.js +3 -1
  36. package/dist/pages/document-plugin-editor.js +46 -0
  37. package/dist/pages/document-plugin-viewer.js +55 -0
  38. package/package.json +1 -1
  39. package/public/index.html +21 -22
  40. package/public/media/dtable-font.css +1566 -0
  41. package/public/media/dtable-fonts/dtable-font.eot +0 -0
  42. package/public/media/dtable-fonts/dtable-font.svg +793 -0
  43. package/public/media/dtable-fonts/dtable-font.ttf +0 -0
  44. package/public/media/dtable-fonts/dtable-font.woff +0 -0
  45. package/public/media/dtable-fonts/dtable-font.woff2 +0 -0
  46. package/public/media/sdoc-editor-font/iconfont.eot +0 -0
  47. package/public/media/sdoc-editor-font/iconfont.svg +6 -0
  48. package/public/media/sdoc-editor-font/iconfont.ttf +0 -0
  49. package/public/media/sdoc-editor-font/iconfont.woff +0 -0
  50. package/public/media/sdoc-editor-font/iconfont.woff2 +0 -0
  51. package/public/media/sdoc-editor-font.css +22 -8
@@ -0,0 +1,62 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import { Editor, Transforms } from '@seafile/slate';
3
+ import slugid from 'slugid';
4
+ import { BLOCKQUOTE, CHECK_LIST_ITEM, COLUMN, IMAGE, ORDERED_LIST, PARAGRAPH, TABLE_CELL, UNORDERED_LIST } from '../../constants/element-type';
5
+ import { focusEditor, getNodeType } from '../../core';
6
+ import Column from './model';
7
+ export const isMenuDisabled = (editor, readonly) => {
8
+ if (readonly) return true;
9
+ if (editor.selection == null) return true;
10
+ const [nodeEntry] = Editor.nodes(editor, {
11
+ match: n => {
12
+ const type = getNodeType(n);
13
+
14
+ // Only available for p and blockquote
15
+ if (type === PARAGRAPH) return true;
16
+ if (type === BLOCKQUOTE) return true;
17
+ if (type === UNORDERED_LIST) return true;
18
+ if (type === ORDERED_LIST) return true;
19
+ if (type === CHECK_LIST_ITEM) return true;
20
+ if (type === IMAGE) return true;
21
+ if (type === TABLE_CELL) return true;
22
+ return false;
23
+ },
24
+ universal: true,
25
+ mode: 'highest' // Match top level
26
+ });
27
+
28
+ // Match to p blockquote, do not disable
29
+ if (nodeEntry) {
30
+ return false;
31
+ }
32
+ return true;
33
+ };
34
+ export const getColumnType = editor => {
35
+ const [match] = Editor.nodes(editor, {
36
+ match: n => getNodeType(n) === COLUMN,
37
+ universal: true
38
+ });
39
+ if (!match) return PARAGRAPH;
40
+ const [n] = match;
41
+ return getNodeType(n);
42
+ };
43
+ export const insertSeaTableColumn = (editor, active) => {
44
+ if (!active) {
45
+ const column = new Column({});
46
+ column.id = slugid.nice();
47
+ Transforms.insertNodes(editor, _objectSpread({}, column));
48
+ }
49
+ focusEditor(editor);
50
+ };
51
+ export const setSeaTableColumn = (editor, data) => {
52
+ Transforms.setNodes(editor, {
53
+ data: data
54
+ }, {
55
+ match: node => node.type === COLUMN,
56
+ at: editor.selection
57
+ });
58
+ };
59
+ export const getColumnByKey = (columns, key) => {
60
+ const column = columns.find(item => item.key === key);
61
+ return column || null;
62
+ };
@@ -0,0 +1,12 @@
1
+ import { COLUMN } from '../../constants/element-type';
2
+ import ColumnMenu from './menu';
3
+ import withColumn from './plugin';
4
+ import renderColumn from './render-elem';
5
+ const ColumnPlugin = {
6
+ type: COLUMN,
7
+ nodeType: 'element',
8
+ editorMenus: [ColumnMenu],
9
+ editorPlugin: withColumn,
10
+ renderElements: [renderColumn]
11
+ };
12
+ export default ColumnPlugin;
@@ -0,0 +1,28 @@
1
+ import React, { useCallback } from 'react';
2
+ import { MenuItem } from '../../../commons';
3
+ import { MENUS_CONFIG_MAP } from '../../../constants/menus-config';
4
+ import { COLUMN } from '../../../constants/element-type';
5
+ import { getColumnType, isMenuDisabled, insertSeaTableColumn } from '../helpers';
6
+ const menuConfig = MENUS_CONFIG_MAP[COLUMN];
7
+ const isActive = editor => {
8
+ return getColumnType(editor) === COLUMN;
9
+ };
10
+ export default function ColumnMenu(_ref) {
11
+ let {
12
+ isRichEditor,
13
+ className,
14
+ readonly,
15
+ editor
16
+ } = _ref;
17
+ const onMousedown = useCallback(event => {
18
+ const active = isActive(editor);
19
+ insertSeaTableColumn(editor, active);
20
+ }, [editor]);
21
+ return /*#__PURE__*/React.createElement(MenuItem, Object.assign({
22
+ isRichEditor: isRichEditor,
23
+ className: className,
24
+ disabled: isMenuDisabled(editor, readonly),
25
+ isActive: isActive(editor),
26
+ onMouseDown: onMousedown
27
+ }, menuConfig));
28
+ }
@@ -0,0 +1,14 @@
1
+ import { generateDefaultText } from '../../core';
2
+ class Column {
3
+ constructor(options) {
4
+ this.type = options.type || 'column';
5
+ this.data = options.data || {
6
+ key: '',
7
+ name: '',
8
+ bold: false,
9
+ italic: false
10
+ };
11
+ this.children = options.children || [generateDefaultText()];
12
+ }
13
+ }
14
+ export default Column;
@@ -0,0 +1,24 @@
1
+ import { ELEMENT_TYPE } from '../../constants';
2
+ const withColumn = editor => {
3
+ const {
4
+ isInline,
5
+ isVoid
6
+ } = editor;
7
+ const newEditor = editor;
8
+ newEditor.isInline = element => {
9
+ const {
10
+ type
11
+ } = element;
12
+ if (type === ELEMENT_TYPE.COLUMN) return true;
13
+ return isInline(element);
14
+ };
15
+ newEditor.isVoid = element => {
16
+ const {
17
+ type
18
+ } = element;
19
+ if (type === ELEMENT_TYPE.COLUMN) return true;
20
+ return isVoid(element);
21
+ };
22
+ return newEditor;
23
+ };
24
+ export default withColumn;
@@ -0,0 +1,111 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import React, { useCallback, useMemo } from 'react';
3
+ import { useReadOnly, useSelected, useSlateStatic } from '@seafile/slate-react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Select } from '../../commons';
6
+ import { COLUMNS_ICON_CONFIG } from './constants/column';
7
+ import { getColumnByKey, setSeaTableColumn } from './helpers';
8
+ const NOT_SUPPORT_COLUMN_TYPES = ['button', 'file'];
9
+ const Column = _ref => {
10
+ let {
11
+ attributes,
12
+ children,
13
+ element
14
+ } = _ref;
15
+ const editor = useSlateStatic();
16
+ const isSelected = useSelected();
17
+ const {
18
+ t
19
+ } = useTranslation();
20
+ const columns = useMemo(() => {
21
+ if (!editor.columns) return [];
22
+ return editor.columns.filter(column => !NOT_SUPPORT_COLUMN_TYPES.includes(column.type));
23
+ }, [editor.columns]);
24
+ const options = useMemo(() => {
25
+ return columns.map(item => {
26
+ const iconClass = COLUMNS_ICON_CONFIG[item.type];
27
+ return {
28
+ value: item.key,
29
+ label: item.name,
30
+ bold: false,
31
+ italic: false,
32
+ iconClass
33
+ };
34
+ });
35
+ }, [columns]);
36
+ const onColumnChanged = useCallback(option => {
37
+ const {
38
+ data
39
+ } = element;
40
+ const {
41
+ value: key,
42
+ label: name,
43
+ bold,
44
+ italic
45
+ } = option;
46
+ const newData = _objectSpread(_objectSpread({}, data), {
47
+ key,
48
+ name,
49
+ bold,
50
+ italic
51
+ });
52
+ setSeaTableColumn(editor, newData);
53
+ }, [editor, element]);
54
+ const defaultValue = useMemo(() => {
55
+ const {
56
+ data
57
+ } = element || {}; // column model
58
+ const column = getColumnByKey(columns, data.key);
59
+ const value = column && column.key || '';
60
+ const optionIndex = options.findIndex(item => item.value === value);
61
+ if (optionIndex === -1) return null;
62
+
63
+ // used the old properties
64
+ const option = options[optionIndex];
65
+ const currentOption = _objectSpread(_objectSpread({}, option), {
66
+ bold: data.bold,
67
+ italic: data.italic
68
+ });
69
+ options.splice(optionIndex, 1, currentOption);
70
+ return currentOption;
71
+ }, [columns, element, options]);
72
+ const props = {
73
+ isSelected: isSelected,
74
+ placeholder: t('Select_field'),
75
+ value: defaultValue,
76
+ options,
77
+ onChange: onColumnChanged
78
+ };
79
+ return /*#__PURE__*/React.createElement("span", attributes, /*#__PURE__*/React.createElement(Select, props), children);
80
+ };
81
+ const renderColumn = (props, editor) => {
82
+ // eslint-disable-next-line react-hooks/rules-of-hooks
83
+ const isReadOnly = useReadOnly();
84
+ if (isReadOnly) {
85
+ const {
86
+ attributes,
87
+ element
88
+ } = props;
89
+ const data = element.data || {};
90
+ const {
91
+ key: columnKey,
92
+ name: columnName,
93
+ bold,
94
+ italic
95
+ } = data;
96
+ let displayValue = columnName ? "{".concat(columnName, "}") : '';
97
+ if (editor.getColumnCellValue) {
98
+ displayValue = editor.getColumnCellValue(columnKey);
99
+ }
100
+ const style = _objectSpread(_objectSpread({}, bold && {
101
+ 'fontWeight': 600
102
+ }), italic && {
103
+ 'fontStyle': 'italic'
104
+ });
105
+ return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes, {
106
+ style: _objectSpread({}, style)
107
+ }), displayValue);
108
+ }
109
+ return /*#__PURE__*/React.createElement(Column, props);
110
+ };
111
+ export default renderColumn;
@@ -13,6 +13,7 @@ export const isMenuDisabled = (editor, readonly) => {
13
13
  universal: true,
14
14
  mode: 'highest'
15
15
  });
16
+ if (!match) return false;
16
17
  const elementType = match[0].type;
17
18
  if (elementType === CODE_BLOCK || elementType === IMAGE) {
18
19
  return true;
@@ -0,0 +1,7 @@
1
+ import { ELEMENT_TYPE } from '../../constants';
2
+ import { renderGroup } from './render-elem';
3
+ const GroupPlugin = {
4
+ type: ELEMENT_TYPE.GROUP,
5
+ renderElements: [renderGroup]
6
+ };
7
+ export default GroupPlugin;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ const Group = _ref => {
3
+ let {
4
+ element,
5
+ attributes,
6
+ children,
7
+ className
8
+ } = _ref;
9
+ return /*#__PURE__*/React.createElement("div", Object.assign({
10
+ "data-id": element.id
11
+ }, attributes, {
12
+ className: className
13
+ }), children);
14
+ };
15
+ export const renderGroup = props => {
16
+ return /*#__PURE__*/React.createElement(Group, props);
17
+ };
@@ -18,8 +18,10 @@ import CalloutPlugin from './callout';
18
18
  import SearchReplacePlugin from './search-replace';
19
19
  import MentionPlugin from './mention';
20
20
  import QuickInsertPlugin from './quick-insert';
21
- const Plugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin];
21
+ import ColumnPlugin from './column';
22
+ import GroupPlugin from './group';
23
+ const Plugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, GroupPlugin];
22
24
  const WikiPlugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, QuickInsertPlugin];
23
25
  const CommentPlugins = [MarkDownPlugin, HtmlPlugin, ParagraphPlugin, TextPlugin, ListPlugin, ImagePlugin, LinkPlugin, MentionPlugin, BlockquotePlugin];
24
26
  export default Plugins;
25
- export { MarkDownPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, HtmlPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, MentionPlugin, QuickInsertPlugin, CommentPlugins, WikiPlugins };
27
+ export { MarkDownPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, HtmlPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, MentionPlugin, QuickInsertPlugin, CommentPlugins, WikiPlugins, ColumnPlugin, GroupPlugin };
@@ -10,6 +10,7 @@ export const isMenuDisabled = (editor, readonly) => {
10
10
  universal: true,
11
11
  mode: 'highest'
12
12
  });
13
+ if (!match) return false;
13
14
  const elementType = match[0].type;
14
15
  // at present, TABLE got its own 'text align'
15
16
  if (elementType === CODE_BLOCK || elementType === TABLE) {
@@ -1,10 +1,11 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import { useReadOnly, useSlateStatic } from '@seafile/slate-react';
3
3
  import { BLOCKQUOTE, LINK, CHECK_LIST_ITEM, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, LIST_ITEM, ORDERED_LIST, PARAGRAPH, UNORDERED_LIST, CODE_BLOCK, CODE_LINE, IMAGE, IMAGE_BLOCK, ELEMENT_TYPE, SDOC_LINK, FILE_LINK, TITLE, SUBTITLE, CALL_OUT, SUPPORTED_SIDE_OPERATION_TYPE, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP, QUICK_INSERT } from '../constants';
4
- import { BlockquotePlugin, LinkPlugin, CheckListPlugin, HeaderPlugin, ListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, MentionPlugin, QuickInsertPlugin } from '../plugins';
4
+ import { BlockquotePlugin, LinkPlugin, CheckListPlugin, HeaderPlugin, ListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, MentionPlugin, QuickInsertPlugin, ColumnPlugin, GroupPlugin } from '../plugins';
5
5
  import { onDragOver, onDragLeave, onDrop } from '../toolbar/side-toolbar/event';
6
6
  import { getParentNode } from '../core';
7
7
  import { setDataRoot, setMouseEnter } from './helper';
8
+ import { COLUMN } from '../constants/element-type';
8
9
  const CustomRenderElement = props => {
9
10
  const editor = useSlateStatic();
10
11
  const readonly = useReadOnly();
@@ -159,6 +160,16 @@ const CustomRenderElement = props => {
159
160
  const [renderQuickInsert] = QuickInsertPlugin.renderElements;
160
161
  return renderQuickInsert(props, editor);
161
162
  }
163
+ case COLUMN:
164
+ {
165
+ const [renderColumn] = ColumnPlugin.renderElements;
166
+ return renderColumn(props, editor);
167
+ }
168
+ case ELEMENT_TYPE.GROUP:
169
+ {
170
+ const [renderGroup] = GroupPlugin.renderElements;
171
+ return renderGroup(props);
172
+ }
162
173
  default:
163
174
  {
164
175
  const [renderParagraph] = ParagraphPlugin.renderElements;
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React from 'react';
2
2
  import useSelectionUpdate from '../../../hooks/use-selection-update';
3
3
  import { ORDERED_LIST, TABLE_CELL, UNORDERED_LIST } from '../../constants';
4
4
  import { MenuGroup } from '../../commons';
@@ -16,10 +16,12 @@ import ActiveTableMenu from '../../plugins/table/menu/active-table-menu';
16
16
  import CalloutMenu from '../../plugins/callout/menu';
17
17
  import SearchReplaceMenu from '../../plugins/search-replace/menu';
18
18
  import { getSelectedNodeByType } from '../../core';
19
+ import ColumnMenu from '../../plugins/column/menu';
19
20
  const HeaderToolbar = _ref => {
20
21
  let {
21
22
  editor,
22
- readonly
23
+ readonly,
24
+ columns
23
25
  } = _ref;
24
26
  useSelectionUpdate();
25
27
  const isSelectTableCell = getSelectedNodeByType(editor, TABLE_CELL);
@@ -63,6 +65,9 @@ const HeaderToolbar = _ref => {
63
65
  }), /*#__PURE__*/React.createElement(CalloutMenu, {
64
66
  editor: editor,
65
67
  readonly: readonly
68
+ }), editor.columns && /*#__PURE__*/React.createElement(ColumnMenu, {
69
+ editor: editor,
70
+ readonly: readonly
66
71
  })), /*#__PURE__*/React.createElement(ActiveTableMenu, {
67
72
  editor: editor,
68
73
  readonly: readonly
@@ -2,19 +2,20 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { useSlateStatic } from '@seafile/slate-react';
4
4
  import { Transforms } from '@seafile/slate';
5
- import { insertElement, isVoidNode } from '../side-toolbar/helpers';
5
+ import { insertElement } from '../side-toolbar/helpers';
6
6
  import { insertTable } from '../../plugins/table/helpers';
7
7
  import TableSizePopover from '../../plugins/table/popover/table-size-popover';
8
8
  import { changeToCodeBlock } from '../../plugins/code-block/helpers';
9
9
  import { toggleList } from '../../plugins/list/transforms';
10
10
  import { setCheckListItemType } from '../../plugins/check-list/helpers';
11
- import { ELEMENT_TYPE, IMAGE, INSERT_POSITION, LINK, LOCAL_IMAGE, PARAGRAPH, SIDE_INSERT_MENUS_CONFIG, TABLE, CODE_BLOCK, CALL_OUT, UNORDERED_LIST, ORDERED_LIST, CHECK_LIST_ITEM } from '../../constants';
11
+ import { ELEMENT_TYPE, IMAGE, INSERT_POSITION, LINK, LOCAL_IMAGE, PARAGRAPH, SIDE_INSERT_MENUS_CONFIG, TABLE, CODE_BLOCK, CALL_OUT, UNORDERED_LIST, ORDERED_LIST, CHECK_LIST_ITEM, QUICK_INSERT } from '../../constants';
12
12
  import EventBus from '../../../utils/event-bus';
13
13
  import { INTERNAL_EVENT } from '../../../constants';
14
14
  import DropdownMenuItem from '../../commons/dropdown-menu-item';
15
15
  import { wrapCallout } from '../../plugins/callout/helper';
16
16
  import keyCodes from '../../../../constants/key-codes';
17
17
  import { SELECTED_ITEM_CLASS_NAME, SHORT_INSERT_ELEMENT_USER_INPUT_MAP } from './const';
18
+ import { getAboveBlockNode } from '../../core';
18
19
  import './style.css';
19
20
  const QuickInsertBlockMenu = _ref => {
20
21
  let {
@@ -81,12 +82,21 @@ const QuickInsertBlockMenu = _ref => {
81
82
  const onInsertCallout = useCallback(type => {
82
83
  if (insertPosition === INSERT_POSITION.CURRENT) {
83
84
  wrapCallout(editor);
85
+ Transforms.removeNodes(editor, {
86
+ match: n => n.type === QUICK_INSERT
87
+ });
84
88
  } else if (insertPosition === INSERT_POSITION.AFTER) {
85
89
  insertElement(editor, type, insertPosition);
86
90
  wrapCallout(editor);
87
91
  }
88
92
  callback && callback();
89
93
  }, [callback, editor, insertPosition]);
94
+ const isDisableCallout = useMemo(() => {
95
+ const callout = getAboveBlockNode(editor, {
96
+ match: n => n.type === ELEMENT_TYPE.CALL_OUT
97
+ });
98
+ return !!callout;
99
+ }, [editor]);
90
100
  const dropDownItems = useMemo(() => {
91
101
  let items = {
92
102
  [IMAGE]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
@@ -122,6 +132,7 @@ const QuickInsertBlockMenu = _ref => {
122
132
  onClick: onInsertCodeBlock
123
133
  }),
124
134
  [CALL_OUT]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
135
+ disabled: isDisableCallout,
125
136
  key: "sdoc-insert-menu-callout",
126
137
  menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.CALL_OUT]),
127
138
  onClick: () => onInsertCallout(PARAGRAPH)
@@ -160,7 +171,7 @@ const QuickInsertBlockMenu = _ref => {
160
171
  });
161
172
  });
162
173
  return items;
163
- }, [createTable, editor, isEmptyNode, onInsert, onInsertCallout, onInsertCheckList, onInsertCodeBlock, onInsertImageToggle, onInsertList, openLinkDialog]);
174
+ }, [createTable, editor, isDisableCallout, isEmptyNode, onInsert, onInsertCallout, onInsertCheckList, onInsertCodeBlock, onInsertImageToggle, onInsertList, openLinkDialog]);
164
175
  const getSelectItemDom = selectIndex => {
165
176
  const dropDownItemWrapper = downDownWrapperRef.current;
166
177
  const currentSelectItem = dropDownItemWrapper.children[selectIndex];
@@ -199,7 +210,11 @@ const QuickInsertBlockMenu = _ref => {
199
210
  if (keyCode === Enter) {
200
211
  e.preventDefault();
201
212
  const item = renderItems[currentSelectIndex];
202
- dropDownItems[item].props.onClick();
213
+ const {
214
+ disabled,
215
+ onClick
216
+ } = dropDownItems[item].props;
217
+ !disabled && onClick();
203
218
  }
204
219
  }, [currentSelectIndex, dropDownItems]);
205
220
  useEffect(() => {
@@ -23,19 +23,33 @@ const SideToolbar = () => {
23
23
  const [isNodeEmpty, setNodeEmpty] = useState(false);
24
24
  const [isShowSideMenu, setShowSideMenu] = useState(false);
25
25
  const [menuPosition, setMenuPosition] = useState({});
26
+ const sideMenuRef = useRef();
26
27
  const onReset = useCallback(() => {
27
28
  setShowSideMenu(false);
28
29
  setMenuPosition({});
29
30
  setSlateNode(null);
30
31
  // eslint-disable-next-line react-hooks/exhaustive-deps
31
32
  }, []);
33
+ const handleClick = useCallback(e => {
34
+ if (!isShowSideMenu) return;
35
+ const isClickSideTool = menuRef.current.contains(e.target);
36
+ if (isClickSideTool) return;
37
+ const {
38
+ sideMenuDom
39
+ } = sideMenuRef.current;
40
+ if (!sideMenuDom) return;
41
+ const isClickContainer = sideMenuDom.contains(e.target);
42
+ if (!isClickContainer) onReset();
43
+ }, [isShowSideMenu, onReset]);
32
44
  useEffect(() => {
33
45
  let observerRefValue;
34
46
  if (isShowSideMenu) {
35
47
  scrollRef.current.addEventListener('scroll', onReset);
48
+ document.addEventListener('click', handleClick);
36
49
  observerRefValue = scrollRef.current;
37
50
  } else {
38
51
  scrollRef.current.removeEventListener('scroll', onReset);
52
+ document.removeEventListener('click', handleClick);
39
53
  observerRefValue = null;
40
54
  }
41
55
  return () => {
@@ -244,7 +258,8 @@ const SideToolbar = () => {
244
258
  slateNode: slateNode,
245
259
  isNodeEmpty: isNodeEmpty,
246
260
  menuPosition: menuPosition,
247
- onReset: onReset
261
+ onReset: onReset,
262
+ ref: sideMenuRef
248
263
  }));
249
264
  };
250
265
  export default SideToolbar;
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
2
2
  import { withTranslation } from 'react-i18next';
3
3
  import { useSlateStatic } from '@seafile/slate-react';
4
4
  import copy from 'copy-to-clipboard';
@@ -13,7 +13,7 @@ import DropdownMenuItem from '../../commons/dropdown-menu-item';
13
13
  import toaster from '../../../../components/toast';
14
14
  import { HEADER1, HEADER2, HEADER3 } from '../../constants';
15
15
  import './side-menu.css';
16
- const SideMenu = _ref => {
16
+ const SideMenu = (_ref, ref) => {
17
17
  let {
18
18
  slateNode,
19
19
  isNodeEmpty,
@@ -65,6 +65,11 @@ const SideMenu = _ref => {
65
65
  setMenuStyle("top: ".concat(top, "px; left: ").concat(menuPosition.left, "px"));
66
66
  // eslint-disable-next-line react-hooks/exhaustive-deps
67
67
  }, [menuPosition, sideMenuRef.current]);
68
+ useImperativeHandle(ref, () => {
69
+ return {
70
+ sideMenuDom: sideMenuRef.current
71
+ };
72
+ }, []);
68
73
  return /*#__PURE__*/React.createElement(ElementPopover, {
69
74
  className: "sdoc-side-menu-popover",
70
75
  style: menuStyle
@@ -129,4 +134,6 @@ const SideMenu = _ref => {
129
134
  onClick: onDelete
130
135
  }))));
131
136
  };
132
- export default withTranslation('sdoc-editor')(SideMenu);
137
+ export default withTranslation('sdoc-editor', {
138
+ withRef: true
139
+ })(forwardRef(SideMenu));
@@ -0,0 +1,9 @@
1
+ import { useCallback, useState } from 'react';
2
+ const useForceUpdate = () => {
3
+ const [, setState] = useState(0);
4
+ const forceUpdate = useCallback(() => {
5
+ setState(n => n + 1);
6
+ }, []);
7
+ return forceUpdate;
8
+ };
9
+ export default useForceUpdate;