@seafile/sdoc-editor 0.5.64 → 0.5.66

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 (50) hide show
  1. package/dist/assets/css/plugin-editor.css +3 -0
  2. package/dist/basic-sdk/assets/css/sdoc-editor-plugins.css +2 -1
  3. package/dist/basic-sdk/extension/commons/index.js +2 -1
  4. package/dist/basic-sdk/extension/commons/select/_option.js +32 -0
  5. package/dist/basic-sdk/extension/commons/select/field-setting.js +101 -0
  6. package/dist/basic-sdk/extension/commons/select/index.js +135 -0
  7. package/dist/basic-sdk/extension/commons/select/menu/index.js +4 -0
  8. package/dist/basic-sdk/extension/commons/select/menu/item.js +32 -0
  9. package/dist/basic-sdk/extension/commons/select/menu/menu.js +27 -0
  10. package/dist/basic-sdk/extension/commons/select/menu/style.css +43 -0
  11. package/dist/basic-sdk/extension/commons/select/style.css +149 -0
  12. package/dist/basic-sdk/extension/constants/element-type.js +1 -0
  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/image/render-elem.js +8 -5
  24. package/dist/basic-sdk/extension/plugins/index.js +2 -1
  25. package/dist/basic-sdk/extension/plugins/quick-insert/render-elem.js +4 -1
  26. package/dist/basic-sdk/extension/plugins/text-align/helpers.js +1 -0
  27. package/dist/basic-sdk/extension/render/custom-element.js +7 -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 +21 -4
  30. package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +16 -1
  31. package/dist/basic-sdk/extension/toolbar/side-toolbar/insert-block-menu.js +3 -1
  32. package/dist/basic-sdk/extension/toolbar/side-toolbar/side-menu.js +11 -3
  33. package/dist/basic-sdk/hooks/use-force-update.js +9 -0
  34. package/dist/index.js +3 -1
  35. package/dist/pages/document-plugin-editor.js +46 -0
  36. package/dist/pages/document-plugin-viewer.js +55 -0
  37. package/package.json +1 -1
  38. package/public/index.html +21 -22
  39. package/public/media/dtable-font.css +1566 -0
  40. package/public/media/dtable-fonts/dtable-font.eot +0 -0
  41. package/public/media/dtable-fonts/dtable-font.svg +793 -0
  42. package/public/media/dtable-fonts/dtable-font.ttf +0 -0
  43. package/public/media/dtable-fonts/dtable-font.woff +0 -0
  44. package/public/media/dtable-fonts/dtable-font.woff2 +0 -0
  45. package/public/media/sdoc-editor-font/iconfont.eot +0 -0
  46. package/public/media/sdoc-editor-font/iconfont.svg +6 -0
  47. package/public/media/sdoc-editor-font/iconfont.ttf +0 -0
  48. package/public/media/sdoc-editor-font/iconfont.woff +0 -0
  49. package/public/media/sdoc-editor-font/iconfont.woff2 +0 -0
  50. 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;
@@ -200,7 +200,8 @@ const Image = _ref => {
200
200
  className: classNames('sdoc-image-wrapper', className)
201
201
  }, attributes, {
202
202
  style: _objectSpread({}, style),
203
- onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected)
203
+ onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected),
204
+ contentEditable: "true"
204
205
  }), /*#__PURE__*/React.createElement("img", {
205
206
  ref: imageRef,
206
207
  src: imagePlaceholder,
@@ -212,9 +213,11 @@ const Image = _ref => {
212
213
  className: classNames('sdoc-image-wrapper', className)
213
214
  }, attributes, {
214
215
  style: _objectSpread({}, style),
215
- onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected)
216
+ onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected),
217
+ contentEditable: "true"
216
218
  }), /*#__PURE__*/React.createElement("span", {
217
- className: "sdoc-image-inner"
219
+ className: "sdoc-image-inner",
220
+ contentEditable: "false"
218
221
  }, /*#__PURE__*/React.createElement("span", {
219
222
  className: "sdoc-image-content"
220
223
  }, /*#__PURE__*/React.createElement("span", {
@@ -311,10 +314,10 @@ export function renderImageBlock(props, editor) {
311
314
  className: "sdoc-image-block-wrapper",
312
315
  style: {
313
316
  display: 'flex',
314
- padding: '5px 0px',
315
317
  justifyContent: "".concat(justifyContent)
316
318
  }
317
319
  }, attributes, {
318
- onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected)
320
+ onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected),
321
+ contentEditable: "false"
319
322
  }), children);
320
323
  }
@@ -18,8 +18,9 @@ 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
+ import ColumnPlugin from './column';
21
22
  const Plugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin];
22
23
  const WikiPlugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, QuickInsertPlugin];
23
24
  const CommentPlugins = [MarkDownPlugin, HtmlPlugin, ParagraphPlugin, TextPlugin, ListPlugin, ImagePlugin, LinkPlugin, MentionPlugin, BlockquotePlugin];
24
25
  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 };
26
+ export { MarkDownPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, HtmlPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, MentionPlugin, QuickInsertPlugin, CommentPlugins, WikiPlugins, ColumnPlugin };
@@ -8,6 +8,7 @@ import { transformToText } from './helper';
8
8
  import { useScrollContext } from '../../../hooks/use-scroll-context';
9
9
  import { INSERT_POSITION } from '../../constants';
10
10
  import QuickInsertBlockMenu from '../../toolbar/insert-element-toolbar';
11
+ import { isVoidNode } from '../../toolbar/side-toolbar/helpers';
11
12
  const RenderQuickInsert = (_ref, editor) => {
12
13
  let {
13
14
  attributes,
@@ -24,6 +25,7 @@ const RenderQuickInsert = (_ref, editor) => {
24
25
  const insertElmRef = useRef(null);
25
26
  const aboveBlockNode = getAboveBlockNode(editor);
26
27
  const searchText = Node.string(element);
28
+ const isEmptyNode = isVoidNode(aboveBlockNode === null || aboveBlockNode === void 0 ? void 0 : aboveBlockNode[0]);
27
29
  const handleClick = useCallback(e => {
28
30
  if (!insertElmRef.current.contains(e.target)) {
29
31
  transformToText(editor, element);
@@ -74,7 +76,8 @@ const RenderQuickInsert = (_ref, editor) => {
74
76
  ref: sideMenuRef,
75
77
  className: "sdoc-side-menu sdoc-dropdown-menu"
76
78
  }, /*#__PURE__*/React.createElement(QuickInsertBlockMenu, {
77
- insertPosition: INSERT_POSITION.AFTER,
79
+ isEmptyNode: isEmptyNode,
80
+ insertPosition: isEmptyNode ? INSERT_POSITION.CURRENT : INSERT_POSITION.AFTER,
78
81
  slateNode: aboveBlockNode === null || aboveBlockNode === void 0 ? void 0 : aboveBlockNode[0],
79
82
  searchText: searchText,
80
83
  callback: handleInsertBlock
@@ -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 } 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,11 @@ 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
+ }
162
168
  default:
163
169
  {
164
170
  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
@@ -8,20 +8,22 @@ 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 {
21
22
  insertPosition,
22
23
  slateNode,
23
24
  searchText,
24
- callback
25
+ callback,
26
+ isEmptyNode
25
27
  } = _ref;
26
28
  const editor = useSlateStatic();
27
29
  const [currentSelectIndex, setCurrentSelectIndex] = useState(0);
@@ -80,12 +82,21 @@ const QuickInsertBlockMenu = _ref => {
80
82
  const onInsertCallout = useCallback(type => {
81
83
  if (insertPosition === INSERT_POSITION.CURRENT) {
82
84
  wrapCallout(editor);
85
+ Transforms.removeNodes(editor, {
86
+ match: n => n.type === QUICK_INSERT
87
+ });
83
88
  } else if (insertPosition === INSERT_POSITION.AFTER) {
84
89
  insertElement(editor, type, insertPosition);
85
90
  wrapCallout(editor);
86
91
  }
87
92
  callback && callback();
88
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]);
89
100
  const dropDownItems = useMemo(() => {
90
101
  let items = {
91
102
  [IMAGE]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
@@ -121,6 +132,7 @@ const QuickInsertBlockMenu = _ref => {
121
132
  onClick: onInsertCodeBlock
122
133
  }),
123
134
  [CALL_OUT]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
135
+ disabled: isDisableCallout,
124
136
  key: "sdoc-insert-menu-callout",
125
137
  menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.CALL_OUT]),
126
138
  onClick: () => onInsertCallout(PARAGRAPH)
@@ -145,6 +157,7 @@ const QuickInsertBlockMenu = _ref => {
145
157
  onClick: onInsertCheckList
146
158
  }),
147
159
  [PARAGRAPH]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
160
+ disabled: isEmptyNode,
148
161
  key: "sdoc-insert-menu-paragraph",
149
162
  menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.PARAGRAPH]),
150
163
  onClick: () => onInsert(ELEMENT_TYPE.PARAGRAPH)
@@ -158,7 +171,7 @@ const QuickInsertBlockMenu = _ref => {
158
171
  });
159
172
  });
160
173
  return items;
161
- }, [createTable, editor, onInsert, onInsertCallout, onInsertCheckList, onInsertCodeBlock, onInsertImageToggle, onInsertList, openLinkDialog]);
174
+ }, [createTable, editor, isDisableCallout, isEmptyNode, onInsert, onInsertCallout, onInsertCheckList, onInsertCodeBlock, onInsertImageToggle, onInsertList, openLinkDialog]);
162
175
  const getSelectItemDom = selectIndex => {
163
176
  const dropDownItemWrapper = downDownWrapperRef.current;
164
177
  const currentSelectItem = dropDownItemWrapper.children[selectIndex];
@@ -197,7 +210,11 @@ const QuickInsertBlockMenu = _ref => {
197
210
  if (keyCode === Enter) {
198
211
  e.preventDefault();
199
212
  const item = renderItems[currentSelectIndex];
200
- dropDownItems[item].props.onClick();
213
+ const {
214
+ disabled,
215
+ onClick
216
+ } = dropDownItems[item].props;
217
+ !disabled && onClick();
201
218
  }
202
219
  }, [currentSelectIndex, dropDownItems]);
203
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;
@@ -16,7 +16,8 @@ import { wrapCallout } from '../../plugins/callout/helper';
16
16
  const InsertBlockMenu = _ref => {
17
17
  let {
18
18
  insertPosition,
19
- slateNode
19
+ slateNode,
20
+ isNodeEmpty
20
21
  } = _ref;
21
22
  const editor = useSlateStatic();
22
23
  const onInsertImageToggle = useCallback(() => {
@@ -113,6 +114,7 @@ const InsertBlockMenu = _ref => {
113
114
  onClick: onInsertCheckList
114
115
  }), [SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.PARAGRAPH], ...SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.HEADER]].map(item => {
115
116
  return /*#__PURE__*/React.createElement(DropdownMenuItem, {
117
+ disabled: isNodeEmpty && item.type === PARAGRAPH,
116
118
  key: item.id,
117
119
  menuConfig: item,
118
120
  onClick: () => onInsert(item.type)
@@ -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
@@ -85,6 +90,7 @@ const SideMenu = _ref => {
85
90
  slateNode: slateNode,
86
91
  onReset: onReset
87
92
  }))), isNodeEmpty && /*#__PURE__*/React.createElement(InsertBlockMenu, {
93
+ isNodeEmpty: isNodeEmpty,
88
94
  slateNode: slateNode
89
95
  }), !isNodeEmpty && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DropdownMenuItem, {
90
96
  menuConfig: {
@@ -128,4 +134,6 @@ const SideMenu = _ref => {
128
134
  onClick: onDelete
129
135
  }))));
130
136
  };
131
- 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;
package/dist/index.js CHANGED
@@ -6,4 +6,6 @@ import DiffViewer from './pages/diff-viewer';
6
6
  import PublishedRevisionViewer from './pages/published-revision-viewer';
7
7
  import WikiViewer from './pages/wiki-viewer';
8
8
  import SdocWikiViewer from './pages/sdoc-wiki-viewer';
9
- export { SDocViewer, SimpleEditor, SimpleViewer, EventBus, EXTERNAL_EVENT, DiffViewer, PublishedRevisionViewer, WikiViewer, SdocWikiViewer };
9
+ import DocumentPluginEditor from './pages/document-plugin-editor';
10
+ import DocumentPluginViewer from './pages/document-plugin-viewer';
11
+ export { SDocViewer, SimpleEditor, SimpleViewer, EventBus, EXTERNAL_EVENT, DiffViewer, PublishedRevisionViewer, WikiViewer, SdocWikiViewer, DocumentPluginEditor, DocumentPluginViewer };
@@ -0,0 +1,46 @@
1
+ import React, { useMemo } from 'react';
2
+ import { withTranslation } from 'react-i18next';
3
+ import context from '../context';
4
+ import { SDocEditor } from '../basic-sdk';
5
+ import { PAGE_EDIT_AREA_WIDTH } from '../basic-sdk/constants';
6
+ import { createDefaultEditor } from '../basic-sdk/extension';
7
+ import withNodeId from '../basic-sdk/node-id';
8
+ import { withSocketIO } from '../basic-sdk/socket';
9
+ import { ColumnPlugin } from '../basic-sdk/extension/plugins';
10
+ import ErrorBoundary from './error-boundary';
11
+ import '../assets/css/simple-editor.css';
12
+ import '../assets/css/plugin-editor.css';
13
+ const DocumentPluginEditor = _ref => {
14
+ let {
15
+ document,
16
+ showOutline,
17
+ scrollRef,
18
+ columns
19
+ } = _ref;
20
+ context.initApi();
21
+ const validEditor = useMemo(() => {
22
+ const withColumnPlugin = ColumnPlugin.editorPlugin;
23
+ const defaultEditor = withColumnPlugin(createDefaultEditor());
24
+ const editorConfig = context.getEditorConfig();
25
+ const newEditor = withNodeId(withSocketIO(defaultEditor, {
26
+ document,
27
+ config: editorConfig
28
+ }));
29
+ const {
30
+ cursors
31
+ } = document;
32
+ newEditor.columns = columns || [];
33
+ newEditor.cursors = cursors || {};
34
+ newEditor.width = PAGE_EDIT_AREA_WIDTH; // default width
35
+ return newEditor;
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, []);
38
+ return /*#__PURE__*/React.createElement(ErrorBoundary, null, /*#__PURE__*/React.createElement(SDocEditor, {
39
+ editor: validEditor,
40
+ scrollRef: scrollRef,
41
+ document: document,
42
+ showComment: false,
43
+ showOutline: showOutline
44
+ }));
45
+ };
46
+ export default withTranslation('sdoc-editor')(DocumentPluginEditor);