@seafile/sdoc-editor 0.5.61 → 0.5.62
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/basic-sdk/assets/css/sdoc-wiki-viewer.css +0 -1
- package/dist/basic-sdk/constants/index.js +1 -0
- package/dist/basic-sdk/editor/sdoc-editor.js +23 -4
- package/dist/basic-sdk/extension/constants/element-type.js +1 -0
- package/dist/basic-sdk/extension/constants/index.js +2 -2
- package/dist/basic-sdk/extension/index.js +11 -1
- package/dist/basic-sdk/extension/plugins/index.js +3 -1
- package/dist/basic-sdk/extension/plugins/quick-insert/helper.js +31 -0
- package/dist/basic-sdk/extension/plugins/quick-insert/index.js +9 -0
- package/dist/basic-sdk/extension/plugins/quick-insert/plugin/index.js +141 -0
- package/dist/basic-sdk/extension/plugins/quick-insert/render-elem.js +83 -0
- package/dist/basic-sdk/extension/render/custom-element.js +7 -2
- package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/const.js +19 -0
- package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/index.js +251 -0
- package/dist/basic-sdk/extension/toolbar/insert-element-toolbar/style.css +3 -0
- package/dist/basic-sdk/extension/toolbar/side-toolbar/helpers.js +5 -3
- package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +1 -1
- package/dist/basic-sdk/utils/dom-utils.js +21 -0
- package/dist/context.js +4 -0
- package/dist/index.js +2 -1
- package/dist/layout/layout.css +1 -1
- package/dist/pages/sdoc-wiki-viewer.js +50 -0
- package/package.json +1 -1
|
@@ -32,6 +32,7 @@ export const REVISION_DIFF_VALUE = '1';
|
|
|
32
32
|
export const PAGE_EDIT_AREA_WIDTH = 672; // 672 = 794 - 2[borderLeft + borderRight] - 120[paddingLeft + paddingRight]
|
|
33
33
|
export const COMMENT_EDITOR_EDIT_AREA_WIDTH = 364;
|
|
34
34
|
export const COMMENT_EDITOR = 'comment_editor';
|
|
35
|
+
export const WIKI_EDITOR = 'wiki_editor';
|
|
35
36
|
export const MODIFY_TYPE = {
|
|
36
37
|
ADD: 'add',
|
|
37
38
|
DELETE: 'delete',
|
|
@@ -23,7 +23,10 @@ const SdocEditor = forwardRef((_ref, ref) => {
|
|
|
23
23
|
editor: propsEditor,
|
|
24
24
|
document,
|
|
25
25
|
isReloading,
|
|
26
|
-
showComment
|
|
26
|
+
showComment,
|
|
27
|
+
isShowHeaderToolbar = true,
|
|
28
|
+
showOutline = true,
|
|
29
|
+
isWikiReadOnly
|
|
27
30
|
} = _ref;
|
|
28
31
|
const validEditor = propsEditor || useMemo(() => {
|
|
29
32
|
const defaultEditor = createDefaultEditor();
|
|
@@ -147,7 +150,7 @@ const SdocEditor = forwardRef((_ref, ref) => {
|
|
|
147
150
|
return /*#__PURE__*/React.createElement(EditorContainer, {
|
|
148
151
|
editor: validEditor,
|
|
149
152
|
readonly: isFreezed
|
|
150
|
-
}, /*#__PURE__*/React.createElement(CollaboratorsProvider, null, /*#__PURE__*/React.createElement(ColorProvider, null, /*#__PURE__*/React.createElement(HeaderToolbar, {
|
|
153
|
+
}, /*#__PURE__*/React.createElement(CollaboratorsProvider, null, /*#__PURE__*/React.createElement(ColorProvider, null, isShowHeaderToolbar && /*#__PURE__*/React.createElement(HeaderToolbar, {
|
|
151
154
|
editor: validEditor,
|
|
152
155
|
readonly: isFreezed
|
|
153
156
|
}), /*#__PURE__*/React.createElement(EditorContent, {
|
|
@@ -164,13 +167,29 @@ const SdocEditor = forwardRef((_ref, ref) => {
|
|
|
164
167
|
}
|
|
165
168
|
const isShowComment = typeof showComment === 'boolean' ? showComment : true;
|
|
166
169
|
const Provider = isShowComment ? CollaboratorsProvider : Fragment;
|
|
170
|
+
if (isWikiReadOnly) {
|
|
171
|
+
return /*#__PURE__*/React.createElement(EditorContainer, {
|
|
172
|
+
editor: validEditor,
|
|
173
|
+
readonly: isWikiReadOnly
|
|
174
|
+
}, /*#__PURE__*/React.createElement(ColorProvider, null, /*#__PURE__*/React.createElement(EditorContent, {
|
|
175
|
+
docValue: slateValue,
|
|
176
|
+
showOutline: showOutline !== null && showOutline !== void 0 ? showOutline : true,
|
|
177
|
+
readonly: isWikiReadOnly,
|
|
178
|
+
editor: validEditor,
|
|
179
|
+
showComment: isShowComment
|
|
180
|
+
}, /*#__PURE__*/React.createElement(ReadOnlyArticle, {
|
|
181
|
+
editor: validEditor,
|
|
182
|
+
slateValue: slateValue,
|
|
183
|
+
showComment: isShowComment
|
|
184
|
+
}))));
|
|
185
|
+
}
|
|
167
186
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(EditorContainer, {
|
|
168
187
|
editor: validEditor
|
|
169
|
-
}, /*#__PURE__*/React.createElement(Provider, null, /*#__PURE__*/React.createElement(ColorProvider, null, /*#__PURE__*/React.createElement(HeaderToolbar, {
|
|
188
|
+
}, /*#__PURE__*/React.createElement(Provider, null, /*#__PURE__*/React.createElement(ColorProvider, null, isShowHeaderToolbar && /*#__PURE__*/React.createElement(HeaderToolbar, {
|
|
170
189
|
editor: validEditor
|
|
171
190
|
}), /*#__PURE__*/React.createElement(EditorContent, {
|
|
172
191
|
docValue: slateValue,
|
|
173
|
-
showOutline: true,
|
|
192
|
+
showOutline: showOutline !== null && showOutline !== void 0 ? showOutline : true,
|
|
174
193
|
editor: validEditor,
|
|
175
194
|
showComment: isShowComment
|
|
176
195
|
}, /*#__PURE__*/React.createElement(EditableArticle, {
|
|
@@ -27,6 +27,7 @@ export const CALL_OUT = 'callout';
|
|
|
27
27
|
export const MENTION = 'mention';
|
|
28
28
|
export const MENTION_TEMP = 'mention_temp';
|
|
29
29
|
export const FILE_LINK_INSET_INPUT_TEMP = 'file_link_insert_input_temp';
|
|
30
|
+
export const QUICK_INSERT = 'quick_insert';
|
|
30
31
|
|
|
31
32
|
// font
|
|
32
33
|
export const FONT_SIZE = 'font-size';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// extension plugin
|
|
2
2
|
import * as ELEMENT_TYPE from './element-type';
|
|
3
3
|
// eslint-disable-next-line no-duplicate-imports
|
|
4
|
-
import { BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP } from './element-type';
|
|
4
|
+
import { BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP, QUICK_INSERT } from './element-type';
|
|
5
5
|
export { DEFAULT_COLORS, STANDARD_COLORS, DEFAULT_RECENT_USED_LIST, DEFAULT_FONT_COLOR, RECENT_USED_HIGHLIGHT_COLORS_KEY, RECENT_USED_FONT_COLORS_KEY, RECENT_USED_TABLE_CELL_BACKGROUND_COLORS_KEY, DEFAULT_LAST_USED_FONT_COLOR, DEFAULT_LAST_USED_HIGHLIGHT_COLOR, DEFAULT_LAST_USED_TABLE_CELL_BACKGROUND_COLOR } from './color';
|
|
6
6
|
export { FONT_SIZE, DEFAULT_FONT, FONT, GOOGLE_FONT_CLASS, RECENT_USED_FONTS_KEY, SDOC_FONT_SIZE } from './font';
|
|
7
7
|
export { DIFF_TYPE, ADDED_STYLE, DELETED_STYLE } from './diff-view';
|
|
@@ -57,4 +57,4 @@ export const MOUSE_ENTER_EVENT_DISABLED_MAP = {
|
|
|
57
57
|
[CALL_OUT]: [CALL_OUT]
|
|
58
58
|
};
|
|
59
59
|
export const ROOT_ELEMENT_TYPES = [PARAGRAPH, TITLE, SUBTITLE, CHECK_LIST_ITEM, ORDERED_LIST, UNORDERED_LIST, BLOCKQUOTE, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, CALL_OUT, TABLE, CODE_BLOCK, IMAGE_BLOCK];
|
|
60
|
-
export { ELEMENT_TYPE, BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP };
|
|
60
|
+
export { ELEMENT_TYPE, BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP, QUICK_INSERT };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createEditor } from '@seafile/slate';
|
|
2
2
|
import { withReact } from '@seafile/slate-react';
|
|
3
3
|
import { withHistory } from '@seafile/slate-history';
|
|
4
|
-
import Plugins, { CommentPlugins } from './plugins';
|
|
4
|
+
import Plugins, { CommentPlugins, WikiPlugins } from './plugins';
|
|
5
5
|
import renderElement from './render/render-element';
|
|
6
6
|
import renderLeaf from './render/render-leaf';
|
|
7
7
|
import { HeaderToolbar, ContextToolbar, SideToolbar } from './toolbar';
|
|
@@ -23,6 +23,16 @@ export const createDefaultEditor = () => {
|
|
|
23
23
|
}, withHistory(withReact(createEditor())));
|
|
24
24
|
return defaultEditor;
|
|
25
25
|
};
|
|
26
|
+
export const createWikiEditor = () => {
|
|
27
|
+
const defaultEditor = WikiPlugins === null || WikiPlugins === void 0 ? void 0 : WikiPlugins.reduce((editor, pluginItem) => {
|
|
28
|
+
const withPlugin = pluginItem.editorPlugin;
|
|
29
|
+
if (withPlugin) {
|
|
30
|
+
return withPlugin(editor);
|
|
31
|
+
}
|
|
32
|
+
return editor;
|
|
33
|
+
}, withHistory(withReact(createEditor())));
|
|
34
|
+
return defaultEditor;
|
|
35
|
+
};
|
|
26
36
|
export const createCommentEditor = () => {
|
|
27
37
|
const defaultEditor = CommentPlugins === null || CommentPlugins === void 0 ? void 0 : CommentPlugins.reduce((editor, pluginItem) => {
|
|
28
38
|
const withPlugin = pluginItem.editorPlugin;
|
|
@@ -17,7 +17,9 @@ import ParagraphPlugin from './paragraph';
|
|
|
17
17
|
import CalloutPlugin from './callout';
|
|
18
18
|
import SearchReplacePlugin from './search-replace';
|
|
19
19
|
import MentionPlugin from './mention';
|
|
20
|
+
import QuickInsertPlugin from './quick-insert';
|
|
20
21
|
const Plugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin];
|
|
22
|
+
const WikiPlugins = [MarkDownPlugin, HtmlPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, QuickInsertPlugin];
|
|
21
23
|
const CommentPlugins = [MarkDownPlugin, HtmlPlugin, ParagraphPlugin, TextPlugin, ListPlugin, ImagePlugin, LinkPlugin, MentionPlugin, BlockquotePlugin];
|
|
22
24
|
export default Plugins;
|
|
23
|
-
export { MarkDownPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, HtmlPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, MentionPlugin, CommentPlugins };
|
|
25
|
+
export { MarkDownPlugin, HeaderPlugin, LinkPlugin, BlockquotePlugin, ListPlugin, CheckListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, TextPlugin, HtmlPlugin, TextAlignPlugin, FontPlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, SearchReplacePlugin, MentionPlugin, QuickInsertPlugin, CommentPlugins, WikiPlugins };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Editor, Node, Transforms } from '@seafile/slate';
|
|
2
|
+
import { QUICK_INSERT } from '../../constants';
|
|
3
|
+
import { generateEmptyElement } from '../../core';
|
|
4
|
+
import { ReactEditor } from '@seafile/slate-react';
|
|
5
|
+
export const getQuickInsertEntity = editor => {
|
|
6
|
+
const [quickInsertEntity] = Editor.nodes(editor, {
|
|
7
|
+
match: n => n.type === QUICK_INSERT
|
|
8
|
+
});
|
|
9
|
+
return quickInsertEntity;
|
|
10
|
+
};
|
|
11
|
+
export const genQuickInsert = () => {
|
|
12
|
+
const quickInsert = generateEmptyElement(QUICK_INSERT);
|
|
13
|
+
return quickInsert;
|
|
14
|
+
};
|
|
15
|
+
export const transformToText = (editor, quickInsertNode) => {
|
|
16
|
+
const path = ReactEditor.findPath(editor, quickInsertNode);
|
|
17
|
+
const text = Node.string(quickInsertNode);
|
|
18
|
+
const [, insertPath] = Editor.next(editor, {
|
|
19
|
+
at: path
|
|
20
|
+
});
|
|
21
|
+
const insertPoint = Editor.start(editor, insertPath);
|
|
22
|
+
const insertPathRef = Editor.pointRef(editor, insertPoint);
|
|
23
|
+
const insertText = '/' + text;
|
|
24
|
+
Transforms.insertText(editor, insertText, {
|
|
25
|
+
at: insertPoint
|
|
26
|
+
});
|
|
27
|
+
Transforms.removeNodes(editor, {
|
|
28
|
+
at: path
|
|
29
|
+
});
|
|
30
|
+
return insertPathRef.unref();
|
|
31
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { QUICK_INSERT } from '../../constants';
|
|
2
|
+
import renderQuickInsert from './render-elem';
|
|
3
|
+
import withQuickInsert from './plugin';
|
|
4
|
+
const QuickInsertPlugin = {
|
|
5
|
+
editorPlugin: withQuickInsert,
|
|
6
|
+
type: QUICK_INSERT,
|
|
7
|
+
renderElements: [renderQuickInsert]
|
|
8
|
+
};
|
|
9
|
+
export default QuickInsertPlugin;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Editor, Node, Range, Transforms } from '@seafile/slate';
|
|
2
|
+
import { QUICK_INSERT } from '../../../constants';
|
|
3
|
+
import { getBeforeText } from '../../list/helpers';
|
|
4
|
+
import { genQuickInsert, getQuickInsertEntity, transformToText } from '../helper';
|
|
5
|
+
import { focusEditor } from '../../../core';
|
|
6
|
+
import { KeyCodes } from '../../../../../constants';
|
|
7
|
+
const withQuickInsert = editor => {
|
|
8
|
+
const {
|
|
9
|
+
insertText,
|
|
10
|
+
onHotKeyDown,
|
|
11
|
+
isInline,
|
|
12
|
+
deleteBackward,
|
|
13
|
+
deleteForward
|
|
14
|
+
} = editor;
|
|
15
|
+
const newEditor = editor;
|
|
16
|
+
newEditor.insertText = text => {
|
|
17
|
+
if (text === '/') {
|
|
18
|
+
// Avoid triggering quick insert when the cursor is in the quick insert
|
|
19
|
+
const isInQuickInsert = getQuickInsertEntity(editor);
|
|
20
|
+
if (isInQuickInsert) return insertText(text);
|
|
21
|
+
const {
|
|
22
|
+
beforeText
|
|
23
|
+
} = getBeforeText(editor);
|
|
24
|
+
// If the previous text ends with a number, do not trigger the quick insert
|
|
25
|
+
const isEndWithNumber = beforeText.match(/\d+$/);
|
|
26
|
+
if (isEndWithNumber) return insertText(text);
|
|
27
|
+
const quickInsert = genQuickInsert();
|
|
28
|
+
return Transforms.insertNodes(editor, quickInsert);
|
|
29
|
+
}
|
|
30
|
+
return insertText(text);
|
|
31
|
+
};
|
|
32
|
+
newEditor.deleteBackward = unit => {
|
|
33
|
+
const quickInsertEntry = getQuickInsertEntity(editor);
|
|
34
|
+
if (quickInsertEntry) {
|
|
35
|
+
const {
|
|
36
|
+
selection
|
|
37
|
+
} = editor;
|
|
38
|
+
if (selection && Range.isCollapsed(selection)) {
|
|
39
|
+
const [node, path] = quickInsertEntry;
|
|
40
|
+
const contentString = Node.string(node);
|
|
41
|
+
if (!contentString) {
|
|
42
|
+
return Transforms.delete(editor, {
|
|
43
|
+
at: path
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Transform to text when delete shortcut prefix
|
|
48
|
+
const isAtStart = Editor.isStart(editor, selection.focus, quickInsertEntry[1]);
|
|
49
|
+
if (isAtStart) {
|
|
50
|
+
const [, insertPath] = Editor.next(editor, {
|
|
51
|
+
at: path
|
|
52
|
+
});
|
|
53
|
+
const insertPoint = Editor.start(editor, insertPath);
|
|
54
|
+
const insertText = Node.string(quickInsertEntry[0]);
|
|
55
|
+
Transforms.insertText(editor, insertText, {
|
|
56
|
+
at: insertPoint
|
|
57
|
+
});
|
|
58
|
+
Transforms.removeNodes(editor, {
|
|
59
|
+
at: path
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return deleteBackward(unit);
|
|
66
|
+
};
|
|
67
|
+
newEditor.deleteForward = unit => {
|
|
68
|
+
const quickInsertEntry = getQuickInsertEntity(editor);
|
|
69
|
+
if (quickInsertEntry) {
|
|
70
|
+
const {
|
|
71
|
+
selection
|
|
72
|
+
} = editor;
|
|
73
|
+
const isAtEnd = Editor.isEnd(editor, selection.focus, quickInsertEntry[1]);
|
|
74
|
+
if (isAtEnd) {
|
|
75
|
+
deleteForward(unit);
|
|
76
|
+
focusEditor(editor, Editor.end(newEditor, quickInsertEntry[1]));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return deleteForward(unit);
|
|
81
|
+
};
|
|
82
|
+
newEditor.onHotKeyDown = event => {
|
|
83
|
+
const quickInsertEntry = getQuickInsertEntity(editor);
|
|
84
|
+
if (quickInsertEntry) {
|
|
85
|
+
const [quickInsertNode, quickInsertPath] = quickInsertEntry;
|
|
86
|
+
const {
|
|
87
|
+
Esc,
|
|
88
|
+
RightArrow,
|
|
89
|
+
LeftArrow
|
|
90
|
+
} = KeyCodes;
|
|
91
|
+
const {
|
|
92
|
+
keyCode
|
|
93
|
+
} = event;
|
|
94
|
+
if ([RightArrow, LeftArrow].includes(keyCode)) {
|
|
95
|
+
const {
|
|
96
|
+
selection
|
|
97
|
+
} = editor;
|
|
98
|
+
const focusPoint = selection.focus;
|
|
99
|
+
if (!selection) return;
|
|
100
|
+
if (!Range.isCollapsed(selection)) return;
|
|
101
|
+
if (keyCode === RightArrow) {
|
|
102
|
+
if (Editor.isEnd(editor, focusPoint, quickInsertPath)) {
|
|
103
|
+
const insertPoint = transformToText(newEditor, quickInsertNode);
|
|
104
|
+
focusEditor(newEditor, insertPoint);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (keyCode === LeftArrow) {
|
|
109
|
+
if (Editor.isStart(editor, focusPoint, quickInsertPath)) {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
transformToText(newEditor, quickInsertNode);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (keyCode === Esc) {
|
|
117
|
+
// Choose the next point to focus
|
|
118
|
+
// Here need offset + 1 as text not include the prefix '/'
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
const insertPoint = transformToText(newEditor, quickInsertNode);
|
|
121
|
+
return focusEditor(newEditor, insertPoint);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return onHotKeyDown && onHotKeyDown(event);
|
|
125
|
+
};
|
|
126
|
+
newEditor.onCompositionStart = event => {
|
|
127
|
+
const quickInsertEntry = getQuickInsertEntity(editor);
|
|
128
|
+
if (quickInsertEntry) {
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
newEditor.isInline = element => {
|
|
134
|
+
if ([QUICK_INSERT].includes(element.type)) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return isInline(element);
|
|
138
|
+
};
|
|
139
|
+
return newEditor;
|
|
140
|
+
};
|
|
141
|
+
export default withQuickInsert;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import InlineBugFixer from '../../commons/Inline-bug-fix-wrapper';
|
|
3
|
+
import { ElementPopover } from '../../commons';
|
|
4
|
+
import { getAboveBlockNode } from '../../core';
|
|
5
|
+
import { ReactEditor } from '@seafile/slate-react';
|
|
6
|
+
import { Node, Transforms } from '@seafile/slate';
|
|
7
|
+
import { transformToText } from './helper';
|
|
8
|
+
import { useScrollContext } from '../../../hooks/use-scroll-context';
|
|
9
|
+
import { INSERT_POSITION } from '../../constants';
|
|
10
|
+
import QuickInsertBlockMenu from '../../toolbar/insert-element-toolbar';
|
|
11
|
+
const RenderQuickInsert = (_ref, editor) => {
|
|
12
|
+
let {
|
|
13
|
+
attributes,
|
|
14
|
+
children,
|
|
15
|
+
element,
|
|
16
|
+
readonly
|
|
17
|
+
} = _ref;
|
|
18
|
+
const [menuStyle, setMenuStyle] = useState({
|
|
19
|
+
top: 0,
|
|
20
|
+
left: 0
|
|
21
|
+
});
|
|
22
|
+
const sideMenuRef = useRef(null);
|
|
23
|
+
const scrollRef = useScrollContext();
|
|
24
|
+
const insertElmRef = useRef(null);
|
|
25
|
+
const aboveBlockNode = getAboveBlockNode(editor);
|
|
26
|
+
const searchText = Node.string(element);
|
|
27
|
+
const handleClick = useCallback(e => {
|
|
28
|
+
if (!insertElmRef.current.contains(e.target)) {
|
|
29
|
+
transformToText(editor, element);
|
|
30
|
+
}
|
|
31
|
+
}, [editor, element]);
|
|
32
|
+
const genStyle = (top, left) => {
|
|
33
|
+
const overflowY = top + sideMenuRef.current.offsetHeight - document.body.clientHeight;
|
|
34
|
+
if (overflowY > 0) {
|
|
35
|
+
top = top - overflowY - 10;
|
|
36
|
+
}
|
|
37
|
+
return "top: ".concat(top, "px; left: ").concat(left, "px");
|
|
38
|
+
};
|
|
39
|
+
const handleInsertMenuStyle = useCallback(() => {
|
|
40
|
+
const currentDom = ReactEditor.toDOMNode(editor, element);
|
|
41
|
+
const {
|
|
42
|
+
left,
|
|
43
|
+
top
|
|
44
|
+
} = currentDom.getBoundingClientRect();
|
|
45
|
+
const style = genStyle(top, left - 10);
|
|
46
|
+
setMenuStyle(style);
|
|
47
|
+
}, [editor, element]);
|
|
48
|
+
const handleScroll = useCallback(e => {
|
|
49
|
+
handleInsertMenuStyle();
|
|
50
|
+
}, [handleInsertMenuStyle]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const scrollDom = scrollRef.current;
|
|
53
|
+
handleInsertMenuStyle();
|
|
54
|
+
document.addEventListener('click', handleClick);
|
|
55
|
+
scrollDom.addEventListener('scroll', handleScroll);
|
|
56
|
+
return () => {
|
|
57
|
+
document.removeEventListener('click', handleClick);
|
|
58
|
+
scrollDom.removeEventListener('scroll', handleScroll);
|
|
59
|
+
};
|
|
60
|
+
}, [editor, element, handleClick, handleInsertMenuStyle, handleScroll, scrollRef]);
|
|
61
|
+
const handleInsertBlock = node => {
|
|
62
|
+
Transforms.delete(editor, {
|
|
63
|
+
at: ReactEditor.findPath(editor, element)
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
67
|
+
ref: insertElmRef
|
|
68
|
+
}, /*#__PURE__*/React.createElement("span", Object.assign({}, attributes, {
|
|
69
|
+
className: ""
|
|
70
|
+
}), /*#__PURE__*/React.createElement(InlineBugFixer, null), /*#__PURE__*/React.createElement("span", null, "/", children), /*#__PURE__*/React.createElement(InlineBugFixer, null), /*#__PURE__*/React.createElement(ElementPopover, {
|
|
71
|
+
className: "sdoc-side-menu-popover",
|
|
72
|
+
style: menuStyle
|
|
73
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
74
|
+
ref: sideMenuRef,
|
|
75
|
+
className: "sdoc-side-menu sdoc-dropdown-menu"
|
|
76
|
+
}, /*#__PURE__*/React.createElement(QuickInsertBlockMenu, {
|
|
77
|
+
insertPosition: INSERT_POSITION.AFTER,
|
|
78
|
+
slateNode: aboveBlockNode === null || aboveBlockNode === void 0 ? void 0 : aboveBlockNode[0],
|
|
79
|
+
searchText: searchText,
|
|
80
|
+
callback: handleInsertBlock
|
|
81
|
+
})))));
|
|
82
|
+
};
|
|
83
|
+
export default RenderQuickInsert;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import { useReadOnly, useSlateStatic } from '@seafile/slate-react';
|
|
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 } from '../constants';
|
|
4
|
-
import { BlockquotePlugin, LinkPlugin, CheckListPlugin, HeaderPlugin, ListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, MentionPlugin } from '../plugins';
|
|
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';
|
|
5
5
|
import { onDragOver, onDragLeave, onDrop } from '../toolbar/side-toolbar/event';
|
|
6
6
|
import { getParentNode } from '../core';
|
|
7
7
|
import { setDataRoot, setMouseEnter } from './helper';
|
|
@@ -154,6 +154,11 @@ const CustomRenderElement = props => {
|
|
|
154
154
|
const [, renderMentionTemporaryInput] = MentionPlugin.renderElements;
|
|
155
155
|
return renderMentionTemporaryInput(props, editor);
|
|
156
156
|
}
|
|
157
|
+
case QUICK_INSERT:
|
|
158
|
+
{
|
|
159
|
+
const [renderQuickInsert] = QuickInsertPlugin.renderElements;
|
|
160
|
+
return renderQuickInsert(props, editor);
|
|
161
|
+
}
|
|
157
162
|
default:
|
|
158
163
|
{
|
|
159
164
|
const [renderParagraph] = ParagraphPlugin.renderElements;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CALL_OUT, CHECK_LIST_ITEM, CODE_BLOCK, IMAGE, LINK, ORDERED_LIST, PARAGRAPH, TABLE, UNORDERED_LIST, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6 } from '../../constants';
|
|
2
|
+
export const SELECTED_ITEM_CLASS_NAME = 'sdoc-dropdown-item-selected';
|
|
3
|
+
export const SHORT_INSERT_ELEMENT_USER_INPUT_MAP = {
|
|
4
|
+
[IMAGE]: 'image',
|
|
5
|
+
[TABLE]: 'table',
|
|
6
|
+
[LINK]: 'link',
|
|
7
|
+
[CODE_BLOCK]: 'code block',
|
|
8
|
+
[CALL_OUT]: 'callout',
|
|
9
|
+
[UNORDERED_LIST]: 'unorder list',
|
|
10
|
+
[ORDERED_LIST]: 'order list',
|
|
11
|
+
[CHECK_LIST_ITEM]: 'check list',
|
|
12
|
+
[PARAGRAPH]: 'paragraph',
|
|
13
|
+
[HEADER1]: 'header 1',
|
|
14
|
+
[HEADER2]: 'header 2',
|
|
15
|
+
[HEADER3]: 'header 3',
|
|
16
|
+
[HEADER4]: 'header 4',
|
|
17
|
+
[HEADER5]: 'header 5',
|
|
18
|
+
[HEADER6]: 'header 6'
|
|
19
|
+
};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useSlateStatic } from '@seafile/slate-react';
|
|
4
|
+
import { Transforms } from '@seafile/slate';
|
|
5
|
+
import { insertElement } from '../side-toolbar/helpers';
|
|
6
|
+
import { insertTable } from '../../plugins/table/helpers';
|
|
7
|
+
import TableSizePopover from '../../plugins/table/popover/table-size-popover';
|
|
8
|
+
import { changeToCodeBlock } from '../../plugins/code-block/helpers';
|
|
9
|
+
import { toggleList } from '../../plugins/list/transforms';
|
|
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';
|
|
12
|
+
import EventBus from '../../../utils/event-bus';
|
|
13
|
+
import { INTERNAL_EVENT } from '../../../constants';
|
|
14
|
+
import DropdownMenuItem from '../../commons/dropdown-menu-item';
|
|
15
|
+
import { wrapCallout } from '../../plugins/callout/helper';
|
|
16
|
+
import keyCodes from '../../../../constants/key-codes';
|
|
17
|
+
import { SELECTED_ITEM_CLASS_NAME, SHORT_INSERT_ELEMENT_USER_INPUT_MAP } from './const';
|
|
18
|
+
import './style.css';
|
|
19
|
+
const QuickInsertBlockMenu = _ref => {
|
|
20
|
+
let {
|
|
21
|
+
insertPosition,
|
|
22
|
+
slateNode,
|
|
23
|
+
searchText,
|
|
24
|
+
callback
|
|
25
|
+
} = _ref;
|
|
26
|
+
const editor = useSlateStatic();
|
|
27
|
+
const [currentSelectIndex, setCurrentSelectIndex] = useState(0);
|
|
28
|
+
const renderItemListRef = useRef([]);
|
|
29
|
+
const downDownWrapperRef = useRef(null);
|
|
30
|
+
const onInsertImageToggle = useCallback(() => {
|
|
31
|
+
const eventBus = EventBus.getInstance();
|
|
32
|
+
if (insertPosition === INSERT_POSITION.CURRENT) {
|
|
33
|
+
Transforms.select(editor, editor.selection.focus);
|
|
34
|
+
}
|
|
35
|
+
eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
|
|
36
|
+
type: LOCAL_IMAGE,
|
|
37
|
+
insertPosition,
|
|
38
|
+
slateNode
|
|
39
|
+
});
|
|
40
|
+
callback && callback();
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, [editor, insertPosition]);
|
|
43
|
+
const createTable = useCallback(size => {
|
|
44
|
+
const newInsertPosition = slateNode.type === ELEMENT_TYPE.LIST_ITEM ? INSERT_POSITION.AFTER : insertPosition;
|
|
45
|
+
insertTable(editor, size, editor.selection, newInsertPosition);
|
|
46
|
+
callback && callback();
|
|
47
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
48
|
+
}, [editor, insertPosition, slateNode]);
|
|
49
|
+
const openLinkDialog = useCallback(() => {
|
|
50
|
+
const eventBus = EventBus.getInstance();
|
|
51
|
+
eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
|
|
52
|
+
type: ELEMENT_TYPE.LINK,
|
|
53
|
+
insertPosition,
|
|
54
|
+
slateNode
|
|
55
|
+
});
|
|
56
|
+
callback && callback();
|
|
57
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
|
+
}, [insertPosition]);
|
|
59
|
+
const onInsertCodeBlock = useCallback(() => {
|
|
60
|
+
const newInsertPosition = slateNode.type === ELEMENT_TYPE.LIST_ITEM ? INSERT_POSITION.AFTER : insertPosition;
|
|
61
|
+
changeToCodeBlock(editor, 'plaintext', newInsertPosition);
|
|
62
|
+
callback && callback();
|
|
63
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
64
|
+
}, [editor, insertPosition, slateNode]);
|
|
65
|
+
const onInsertList = useCallback(type => {
|
|
66
|
+
toggleList(editor, type, insertPosition);
|
|
67
|
+
callback && callback();
|
|
68
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69
|
+
}, [editor, insertPosition, slateNode]);
|
|
70
|
+
const onInsertCheckList = useCallback(() => {
|
|
71
|
+
setCheckListItemType(editor, ELEMENT_TYPE.CHECK_LIST_ITEM, insertPosition);
|
|
72
|
+
callback && callback();
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
+
}, [editor, insertPosition, slateNode]);
|
|
75
|
+
const onInsert = useCallback(type => {
|
|
76
|
+
insertElement(editor, type, insertPosition);
|
|
77
|
+
callback && callback();
|
|
78
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
79
|
+
}, [editor, insertPosition, slateNode]);
|
|
80
|
+
const onInsertCallout = useCallback(type => {
|
|
81
|
+
if (insertPosition === INSERT_POSITION.CURRENT) {
|
|
82
|
+
wrapCallout(editor);
|
|
83
|
+
} else if (insertPosition === INSERT_POSITION.AFTER) {
|
|
84
|
+
insertElement(editor, type, insertPosition);
|
|
85
|
+
wrapCallout(editor);
|
|
86
|
+
}
|
|
87
|
+
callback && callback();
|
|
88
|
+
}, [callback, editor, insertPosition]);
|
|
89
|
+
const dropDownItems = useMemo(() => {
|
|
90
|
+
let items = {
|
|
91
|
+
[IMAGE]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
92
|
+
key: "sdoc-insert-menu-image",
|
|
93
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.IMAGE]),
|
|
94
|
+
onClick: onInsertImageToggle
|
|
95
|
+
}),
|
|
96
|
+
[TABLE]:
|
|
97
|
+
/*#__PURE__*/
|
|
98
|
+
// eslint-disable-next-line react/jsx-indent
|
|
99
|
+
React.createElement(DropdownMenuItem, {
|
|
100
|
+
key: "sdoc-insert-menu-tabe",
|
|
101
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.TABLE]),
|
|
102
|
+
className: "pr-2"
|
|
103
|
+
}, /*#__PURE__*/React.createElement("i", {
|
|
104
|
+
className: "sdocfont sdoc-right-slide sdoc-dropdown-item-right-icon"
|
|
105
|
+
}), /*#__PURE__*/React.createElement(TableSizePopover, {
|
|
106
|
+
editor: editor,
|
|
107
|
+
target: "sdoc-side-menu-item-table",
|
|
108
|
+
trigger: "hover",
|
|
109
|
+
placement: "right-start",
|
|
110
|
+
popperClassName: "sdoc-side-menu-table-size",
|
|
111
|
+
createTable: createTable
|
|
112
|
+
})),
|
|
113
|
+
[LINK]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
114
|
+
key: "sdoc-insert-menu-link",
|
|
115
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.LINK]),
|
|
116
|
+
onClick: openLinkDialog
|
|
117
|
+
}),
|
|
118
|
+
[CODE_BLOCK]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
119
|
+
key: "sdoc-insert-menu-code-block",
|
|
120
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.CODE_BLOCK]),
|
|
121
|
+
onClick: onInsertCodeBlock
|
|
122
|
+
}),
|
|
123
|
+
[CALL_OUT]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
124
|
+
key: "sdoc-insert-menu-callout",
|
|
125
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.CALL_OUT]),
|
|
126
|
+
onClick: () => onInsertCallout(PARAGRAPH)
|
|
127
|
+
}),
|
|
128
|
+
[UNORDERED_LIST]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
129
|
+
key: "sdoc-insert-menu-unorder-list",
|
|
130
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.UNORDERED_LIST]),
|
|
131
|
+
onClick: () => {
|
|
132
|
+
onInsertList(ELEMENT_TYPE.UNORDERED_LIST);
|
|
133
|
+
}
|
|
134
|
+
}),
|
|
135
|
+
[ORDERED_LIST]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
136
|
+
key: "sdoc-insert-menu-order-list",
|
|
137
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.ORDERED_LIST]),
|
|
138
|
+
onClick: () => {
|
|
139
|
+
onInsertList(ELEMENT_TYPE.ORDERED_LIST);
|
|
140
|
+
}
|
|
141
|
+
}),
|
|
142
|
+
[CHECK_LIST_ITEM]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
143
|
+
key: "sdoc-insert-menu-check-list",
|
|
144
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.CHECK_LIST_ITEM]),
|
|
145
|
+
onClick: onInsertCheckList
|
|
146
|
+
}),
|
|
147
|
+
[PARAGRAPH]: /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
148
|
+
key: "sdoc-insert-menu-paragraph",
|
|
149
|
+
menuConfig: _objectSpread({}, SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.PARAGRAPH]),
|
|
150
|
+
onClick: () => onInsert(ELEMENT_TYPE.PARAGRAPH)
|
|
151
|
+
})
|
|
152
|
+
};
|
|
153
|
+
SIDE_INSERT_MENUS_CONFIG[ELEMENT_TYPE.HEADER].forEach(item => {
|
|
154
|
+
items[item.id.toLowerCase()] = /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
155
|
+
key: item.id,
|
|
156
|
+
menuConfig: item,
|
|
157
|
+
onClick: () => onInsert(item.type)
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
return items;
|
|
161
|
+
}, [createTable, editor, onInsert, onInsertCallout, onInsertCheckList, onInsertCodeBlock, onInsertImageToggle, onInsertList, openLinkDialog]);
|
|
162
|
+
const getSelectItemDom = selectIndex => {
|
|
163
|
+
const dropDownItemWrapper = downDownWrapperRef.current;
|
|
164
|
+
const currentSelectItem = dropDownItemWrapper.children[selectIndex];
|
|
165
|
+
return currentSelectItem;
|
|
166
|
+
};
|
|
167
|
+
const handleKeyDown = useCallback(e => {
|
|
168
|
+
const {
|
|
169
|
+
UpArrow,
|
|
170
|
+
DownArrow,
|
|
171
|
+
Enter
|
|
172
|
+
} = keyCodes;
|
|
173
|
+
const renderItems = Reflect.ownKeys(dropDownItems);
|
|
174
|
+
const {
|
|
175
|
+
keyCode
|
|
176
|
+
} = e;
|
|
177
|
+
if (keyCode === UpArrow) {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
const currentSelectItem = getSelectItemDom(currentSelectIndex);
|
|
180
|
+
if (currentSelectItem) currentSelectItem.classList.remove(SELECTED_ITEM_CLASS_NAME);
|
|
181
|
+
if (currentSelectIndex > 0) {
|
|
182
|
+
setCurrentSelectIndex(currentSelectIndex - 1);
|
|
183
|
+
} else {
|
|
184
|
+
setCurrentSelectIndex(renderItems.length - 1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (keyCode === DownArrow) {
|
|
188
|
+
e.preventDefault();
|
|
189
|
+
const currentSelectItem = getSelectItemDom(currentSelectIndex);
|
|
190
|
+
if (currentSelectItem) currentSelectItem.classList.remove(SELECTED_ITEM_CLASS_NAME);
|
|
191
|
+
if (currentSelectIndex < renderItems.length - 1) {
|
|
192
|
+
setCurrentSelectIndex(currentSelectIndex + 1);
|
|
193
|
+
} else {
|
|
194
|
+
setCurrentSelectIndex(0);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (keyCode === Enter) {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
const item = renderItems[currentSelectIndex];
|
|
200
|
+
dropDownItems[item].props.onClick();
|
|
201
|
+
}
|
|
202
|
+
}, [currentSelectIndex, dropDownItems]);
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
205
|
+
return () => {
|
|
206
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
207
|
+
};
|
|
208
|
+
}, [handleKeyDown]);
|
|
209
|
+
const clearSelectStyle = useCallback(() => {
|
|
210
|
+
const domList = Array.from(downDownWrapperRef.current.children);
|
|
211
|
+
domList.forEach(dom => dom.classList.remove(SELECTED_ITEM_CLASS_NAME));
|
|
212
|
+
}, []);
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
clearSelectStyle();
|
|
215
|
+
const currentSelectItem = getSelectItemDom(currentSelectIndex);
|
|
216
|
+
if (currentSelectItem) {
|
|
217
|
+
currentSelectItem.classList.add(SELECTED_ITEM_CLASS_NAME);
|
|
218
|
+
}
|
|
219
|
+
}, [clearSelectStyle, currentSelectIndex, dropDownItems]);
|
|
220
|
+
if (searchText) {
|
|
221
|
+
const renderList = [];
|
|
222
|
+
for (const key in dropDownItems) {
|
|
223
|
+
if (Object.hasOwnProperty.call(dropDownItems, key)) {
|
|
224
|
+
const inputText = SHORT_INSERT_ELEMENT_USER_INPUT_MAP[key];
|
|
225
|
+
if (inputText.toLowerCase().indexOf(searchText.toLowerCase()) !== -1) {
|
|
226
|
+
renderList.push(dropDownItems[key]);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!renderList.length) {
|
|
231
|
+
renderList.push( /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
232
|
+
key: "sdoc-insert-menu-no-result",
|
|
233
|
+
menuConfig: {
|
|
234
|
+
text: 'Search_not_found'
|
|
235
|
+
}
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
renderItemListRef.current = renderList;
|
|
239
|
+
} else {
|
|
240
|
+
// setRenderItemList(dropDownItems);
|
|
241
|
+
renderItemListRef.current = Object.values(dropDownItems);
|
|
242
|
+
}
|
|
243
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
244
|
+
className: "sdoc-insert-element-toolbar",
|
|
245
|
+
ref: downDownWrapperRef
|
|
246
|
+
}, renderItemListRef.current);
|
|
247
|
+
};
|
|
248
|
+
QuickInsertBlockMenu.defaultProps = {
|
|
249
|
+
insertPosition: INSERT_POSITION.CURRENT
|
|
250
|
+
};
|
|
251
|
+
export default QuickInsertBlockMenu;
|
|
@@ -9,6 +9,7 @@ import { setClipboardCodeBlockData } from '../../plugins/code-block/helpers';
|
|
|
9
9
|
import { ORDERED_LIST, UNORDERED_LIST, PARAGRAPH, CHECK_LIST_ITEM, IMAGE, TABLE, CODE_BLOCK, BLOCKQUOTE, LIST_ITEM_CORRELATION_TYPE, ADD_POSITION_OFFSET_TYPE, INSERT_POSITION, ELEMENT_TYPE, CALL_OUT } from '../../constants';
|
|
10
10
|
import { EMPTY_SELECTED_RANGE } from '../../plugins/table/constants';
|
|
11
11
|
import { unwrapCallout, wrapCallout } from '../../plugins/callout/helper';
|
|
12
|
+
import { getHeaderHeight } from '../../../utils/dom-utils';
|
|
12
13
|
export const onSetNodeType = (editor, element, type) => {
|
|
13
14
|
if (!type) return;
|
|
14
15
|
if (type === CALL_OUT) {
|
|
@@ -84,17 +85,18 @@ export const onDeleteNode = (editor, element) => {
|
|
|
84
85
|
at: path
|
|
85
86
|
});
|
|
86
87
|
};
|
|
87
|
-
export const getDomTopHeight = (dom, slateNode) => {
|
|
88
|
+
export const getDomTopHeight = (editor, dom, slateNode) => {
|
|
88
89
|
const rect = dom.getBoundingClientRect();
|
|
89
90
|
let offsetY = 0;
|
|
91
|
+
const iconOffset = 4; // ensure icon is in the middle of the line
|
|
92
|
+
const headerHeight = getHeaderHeight(editor);
|
|
90
93
|
const paddingTop = parseFloat(window.getComputedStyle(dom).getPropertyValue('padding-top'));
|
|
91
94
|
const lineHeight = parseFloat(window.getComputedStyle(dom).getPropertyValue('line-height'));
|
|
92
95
|
const disToolBarHeight = 12; // side toolbar icon is 12 px
|
|
93
96
|
if (ADD_POSITION_OFFSET_TYPE.includes(slateNode.type)) {
|
|
94
97
|
offsetY = lineHeight / 2 + paddingTop - disToolBarHeight / 2;
|
|
95
98
|
}
|
|
96
|
-
|
|
97
|
-
return rect.y - HEADER_HEIGHT + offsetY;
|
|
99
|
+
return rect.y - headerHeight + offsetY - iconOffset;
|
|
98
100
|
};
|
|
99
101
|
export const isVoidNode = node => {
|
|
100
102
|
if (!node) return true;
|
|
@@ -56,7 +56,7 @@ const SideToolbar = () => {
|
|
|
56
56
|
dom = dom.parentNode;
|
|
57
57
|
}
|
|
58
58
|
const node = ReactEditor.toSlateNode(editor, dom);
|
|
59
|
-
const top = getDomTopHeight(dom, node);
|
|
59
|
+
const top = getDomTopHeight(editor, dom, node);
|
|
60
60
|
const isEmpty = isVoidNode(node);
|
|
61
61
|
setSidePosition({
|
|
62
62
|
top: top + scrollRef.current.scrollTop,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WIKI_EDITOR } from '../constants';
|
|
1
2
|
export const getDomHeight = dom => {
|
|
2
3
|
const styles = window.getComputedStyle(dom);
|
|
3
4
|
const rect = dom.getBoundingClientRect();
|
|
@@ -44,4 +45,24 @@ export const getCursorPosition = function () {
|
|
|
44
45
|
x: x,
|
|
45
46
|
y: y
|
|
46
47
|
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the height of the header of the editor
|
|
52
|
+
* @param {Editor} editor
|
|
53
|
+
* @returns {Number} height of the header
|
|
54
|
+
*/
|
|
55
|
+
export const getHeaderHeight = editor => {
|
|
56
|
+
const {
|
|
57
|
+
editorType
|
|
58
|
+
} = editor;
|
|
59
|
+
switch (editorType) {
|
|
60
|
+
case WIKI_EDITOR:
|
|
61
|
+
// sdoc-editor-page-header height = 49
|
|
62
|
+
return 49;
|
|
63
|
+
default:
|
|
64
|
+
// sdoc-editor-page-header height = 56
|
|
65
|
+
// sdoc-editor-toolbar height = 37
|
|
66
|
+
return 56 + 37;
|
|
67
|
+
}
|
|
47
68
|
};
|
package/dist/context.js
CHANGED
|
@@ -282,6 +282,10 @@ class Context {
|
|
|
282
282
|
const docUuid = this.getDocUuid();
|
|
283
283
|
return this.api.readAllNotifications(docUuid);
|
|
284
284
|
}
|
|
285
|
+
updateConfigUuid(docUuid) {
|
|
286
|
+
if (!this.config) return;
|
|
287
|
+
this.config['docUuid'] = docUuid;
|
|
288
|
+
}
|
|
285
289
|
}
|
|
286
290
|
const context = new Context();
|
|
287
291
|
export default context;
|
package/dist/index.js
CHANGED
|
@@ -5,4 +5,5 @@ import SimpleViewer from './pages/simple-viewer';
|
|
|
5
5
|
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 };
|
package/dist/layout/layout.css
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { withTranslation } from 'react-i18next';
|
|
3
|
+
import context from '../context';
|
|
4
|
+
import ErrorBoundary from './error-boundary';
|
|
5
|
+
import { SDocEditor } from '../basic-sdk';
|
|
6
|
+
import { PAGE_EDIT_AREA_WIDTH, WIKI_EDITOR } from '../basic-sdk/constants';
|
|
7
|
+
import { createWikiEditor } from '../basic-sdk/extension';
|
|
8
|
+
import withNodeId from '../basic-sdk/node-id';
|
|
9
|
+
import { withSocketIO } from '../basic-sdk/socket';
|
|
10
|
+
import '../assets/css/simple-viewer.css';
|
|
11
|
+
const SdocWikiViewer = _ref => {
|
|
12
|
+
let {
|
|
13
|
+
document,
|
|
14
|
+
showOutline,
|
|
15
|
+
scrollRef,
|
|
16
|
+
docUuid,
|
|
17
|
+
isWikiReadOnly
|
|
18
|
+
} = _ref;
|
|
19
|
+
context.initApi();
|
|
20
|
+
const validEditor = useMemo(() => {
|
|
21
|
+
const defaultEditor = createWikiEditor();
|
|
22
|
+
// getEditorConfig cashe the config, so we need to update the uuid,for wiki editor
|
|
23
|
+
docUuid && context.updateConfigUuid(docUuid);
|
|
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.cursors = cursors || {};
|
|
33
|
+
newEditor.width = PAGE_EDIT_AREA_WIDTH; // default width
|
|
34
|
+
newEditor.editorType = WIKI_EDITOR;
|
|
35
|
+
return newEditor;
|
|
36
|
+
|
|
37
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
38
|
+
}, []);
|
|
39
|
+
return /*#__PURE__*/React.createElement(ErrorBoundary, null, /*#__PURE__*/React.createElement(SDocEditor, {
|
|
40
|
+
showComment: false,
|
|
41
|
+
document: document,
|
|
42
|
+
showOutline: showOutline,
|
|
43
|
+
scrollRef: scrollRef,
|
|
44
|
+
docUuid: docUuid,
|
|
45
|
+
isShowHeaderToolbar: false,
|
|
46
|
+
editor: validEditor,
|
|
47
|
+
isWikiReadOnly: isWikiReadOnly
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
50
|
+
export default withTranslation('sdoc-editor')(SdocWikiViewer);
|