@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.
- package/dist/assets/css/plugin-editor.css +3 -0
- package/dist/basic-sdk/extension/commons/index.js +2 -1
- package/dist/basic-sdk/extension/commons/select/_option.js +32 -0
- package/dist/basic-sdk/extension/commons/select/field-setting.js +101 -0
- package/dist/basic-sdk/extension/commons/select/index.js +135 -0
- package/dist/basic-sdk/extension/commons/select/menu/index.js +4 -0
- package/dist/basic-sdk/extension/commons/select/menu/item.js +32 -0
- package/dist/basic-sdk/extension/commons/select/menu/menu.js +27 -0
- package/dist/basic-sdk/extension/commons/select/menu/style.css +43 -0
- package/dist/basic-sdk/extension/commons/select/style.css +149 -0
- package/dist/basic-sdk/extension/constants/element-type.js +4 -0
- package/dist/basic-sdk/extension/constants/index.js +2 -2
- package/dist/basic-sdk/extension/constants/menus-config.js +6 -1
- package/dist/basic-sdk/extension/plugins/column/constants/cell-types.js +29 -0
- package/dist/basic-sdk/extension/plugins/column/constants/column.js +28 -0
- package/dist/basic-sdk/extension/plugins/column/helpers.js +62 -0
- package/dist/basic-sdk/extension/plugins/column/index.js +12 -0
- package/dist/basic-sdk/extension/plugins/column/menu/index.js +28 -0
- package/dist/basic-sdk/extension/plugins/column/model.js +14 -0
- package/dist/basic-sdk/extension/plugins/column/plugin.js +24 -0
- package/dist/basic-sdk/extension/plugins/column/render-elem.js +111 -0
- package/dist/basic-sdk/extension/plugins/font/helpers.js +1 -0
- package/dist/basic-sdk/extension/plugins/group/index.js +7 -0
- package/dist/basic-sdk/extension/plugins/group/render-elem.js +17 -0
- package/dist/basic-sdk/extension/plugins/index.js +4 -2
- package/dist/basic-sdk/extension/plugins/text-align/helpers.js +1 -0
- package/dist/basic-sdk/extension/render/custom-element.js +12 -1
- package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +7 -2
- package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/index.js +19 -4
- package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +16 -1
- package/dist/basic-sdk/extension/toolbar/side-toolbar/side-menu.js +10 -3
- package/dist/basic-sdk/hooks/use-force-update.js +9 -0
- package/dist/basic-sdk/utils/rebase.js +93 -14
- package/dist/components/doc-operations/revision-operations/index.js +1 -0
- package/dist/index.js +3 -1
- package/dist/pages/document-plugin-editor.js +46 -0
- package/dist/pages/document-plugin-viewer.js +55 -0
- package/package.json +1 -1
- package/public/index.html +21 -22
- package/public/media/dtable-font.css +1566 -0
- package/public/media/dtable-fonts/dtable-font.eot +0 -0
- package/public/media/dtable-fonts/dtable-font.svg +793 -0
- package/public/media/dtable-fonts/dtable-font.ttf +0 -0
- package/public/media/dtable-fonts/dtable-font.woff +0 -0
- package/public/media/dtable-fonts/dtable-font.woff2 +0 -0
- package/public/media/sdoc-editor-font/iconfont.eot +0 -0
- package/public/media/sdoc-editor-font/iconfont.svg +6 -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 +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;
|
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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'
|
|
137
|
+
export default withTranslation('sdoc-editor', {
|
|
138
|
+
withRef: true
|
|
139
|
+
})(forwardRef(SideMenu));
|