@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.
- package/dist/assets/css/plugin-editor.css +3 -0
- package/dist/basic-sdk/assets/css/sdoc-editor-plugins.css +2 -1
- 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 +1 -0
- 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/image/render-elem.js +8 -5
- package/dist/basic-sdk/extension/plugins/index.js +2 -1
- package/dist/basic-sdk/extension/plugins/quick-insert/render-elem.js +4 -1
- package/dist/basic-sdk/extension/plugins/text-align/helpers.js +1 -0
- package/dist/basic-sdk/extension/render/custom-element.js +7 -1
- package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +7 -2
- package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/index.js +21 -4
- package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +16 -1
- package/dist/basic-sdk/extension/toolbar/side-toolbar/insert-block-menu.js +3 -1
- package/dist/basic-sdk/extension/toolbar/side-toolbar/side-menu.js +11 -3
- package/dist/basic-sdk/hooks/use-force-update.js +9 -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;
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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'
|
|
137
|
+
export default withTranslation('sdoc-editor', {
|
|
138
|
+
withRef: true
|
|
139
|
+
})(forwardRef(SideMenu));
|
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
|
-
|
|
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);
|