@seafile/sdoc-editor 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/assets/css/simple-editor.css +15 -0
  2. package/dist/basic-sdk/assets/css/layout.css +15 -0
  3. package/dist/basic-sdk/comment/components/comment-editor.js +49 -33
  4. package/dist/basic-sdk/comment/components/comment-item-content.js +11 -3
  5. package/dist/basic-sdk/comment/components/comment-item-reply.js +13 -5
  6. package/dist/basic-sdk/comment/components/comment-item-wrapper.js +1 -1
  7. package/dist/basic-sdk/comment/components/comment-list.css +27 -19
  8. package/dist/basic-sdk/comment/components/comment-list.js +0 -1
  9. package/dist/basic-sdk/comment/components/global-comment/index.css +2 -6
  10. package/dist/basic-sdk/comment/utils/index.js +5 -5
  11. package/dist/basic-sdk/constants/index.js +5 -2
  12. package/dist/basic-sdk/editor/comment-article.js +175 -0
  13. package/dist/basic-sdk/editor/editable-article.js +2 -1
  14. package/dist/basic-sdk/editor/sdoc-comment-editor.js +108 -0
  15. package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +12 -7
  16. package/dist/basic-sdk/extension/commons/menu/menu-item.js +1 -1
  17. package/dist/basic-sdk/extension/constants/element-type.js +3 -1
  18. package/dist/basic-sdk/extension/constants/index.js +2 -2
  19. package/dist/basic-sdk/extension/constants/menus-config.js +2 -2
  20. package/dist/basic-sdk/extension/index.js +11 -1
  21. package/dist/basic-sdk/extension/plugins/blockquote/plugin.js +3 -3
  22. package/dist/basic-sdk/extension/plugins/callout/helper.js +5 -2
  23. package/dist/basic-sdk/extension/plugins/image/helpers.js +6 -3
  24. package/dist/basic-sdk/extension/plugins/image/menu/index.js +25 -4
  25. package/dist/basic-sdk/extension/plugins/index.js +3 -1
  26. package/dist/basic-sdk/extension/plugins/link/menu/index.js +22 -3
  27. package/dist/basic-sdk/extension/plugins/markdown/plugin.js +17 -6
  28. package/dist/basic-sdk/extension/plugins/mention/helper.js +142 -0
  29. package/dist/basic-sdk/extension/plugins/mention/index.js +10 -0
  30. package/dist/basic-sdk/extension/plugins/mention/plugin.js +258 -0
  31. package/dist/basic-sdk/{comment/components/comment-input → extension/plugins/mention/render-elem}/comment-participant-item.js +1 -1
  32. package/dist/basic-sdk/{comment/components/comment-input → extension/plugins/mention/render-elem}/index.css +22 -1
  33. package/dist/basic-sdk/extension/plugins/mention/render-elem/index.js +51 -0
  34. package/dist/basic-sdk/extension/plugins/mention/render-elem/participant-popover.js +219 -0
  35. package/dist/basic-sdk/extension/plugins/paragraph/helper.js +10 -0
  36. package/dist/basic-sdk/extension/plugins/paragraph/render-elem.js +9 -3
  37. package/dist/basic-sdk/extension/plugins/text-style/menu/comemnt-editor-menu.js +68 -0
  38. package/dist/basic-sdk/extension/plugins/text-style/menu/index.js +19 -8
  39. package/dist/basic-sdk/extension/render/custom-element.js +12 -2
  40. package/dist/basic-sdk/extension/toolbar/comment-editor-toolbar/index.js +45 -0
  41. package/dist/basic-sdk/utils/diff.js +13 -0
  42. package/dist/basic-sdk/utils/event-handler.js +21 -0
  43. package/dist/components/doc-operations/revision-operations/changes-count/index.js +19 -9
  44. package/dist/slate-convert/md-to-slate/transform.js +17 -0
  45. package/dist/slate-convert/slate-to-md/transform.js +28 -2
  46. package/package.json +1 -1
  47. package/dist/basic-sdk/comment/components/comment-input/helpers.js +0 -15
  48. package/dist/basic-sdk/comment/components/comment-input/index.js +0 -306
@@ -0,0 +1,258 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import { Editor, Node, Range, Text, Transforms } from '@seafile/slate';
3
+ import EventBus from '../../../utils/event-bus';
4
+ import { INTERNAL_EVENT } from '../../../constants';
5
+ import { MENTION, MENTION_TEMP } from '../../constants/element-type';
6
+ import { insertTemporaryMentionInput, getMentionTempIptEntry, transformToText, getMentionEntry, getPrevMentionIptEntry } from './helper';
7
+ import { KeyCodes } from '../../../../constants';
8
+ import { focusEditor } from '../../core';
9
+ const withMention = editor => {
10
+ const {
11
+ insertText,
12
+ onHotKeyDown,
13
+ isInline,
14
+ deleteBackward,
15
+ deleteForward,
16
+ normalizeNode
17
+ } = editor;
18
+ const newEditor = editor;
19
+ const eventBus = EventBus.getInstance();
20
+ newEditor.insertText = text => {
21
+ const {
22
+ selection
23
+ } = editor;
24
+ if (text === '@' && !getMentionTempIptEntry(editor)) {
25
+ insertTemporaryMentionInput(newEditor);
26
+ const {
27
+ anchor
28
+ } = selection;
29
+ const path = Editor.path(editor, anchor);
30
+ const abovePath = path.slice(0, path.length - 1);
31
+ const focusPath = abovePath.concat(path.at(-1) + 1);
32
+ focusEditor(editor, focusPath);
33
+ return;
34
+ }
35
+ const prevNodeEntry = Editor.previous(editor);
36
+ if (prevNodeEntry) {
37
+ const aboveNodeEntry = Editor.above(editor, {
38
+ match: n => n.type === MENTION_TEMP,
39
+ at: prevNodeEntry[1]
40
+ });
41
+ if (aboveNodeEntry) {
42
+ var _text$match;
43
+ const inputCnCharacter = (_text$match = text.match(/^[\u4e00-\u9fa5]+$/)) === null || _text$match === void 0 ? void 0 : _text$match.input;
44
+ if (inputCnCharacter) {
45
+ const insertPoint = Editor.end(editor, aboveNodeEntry[1]);
46
+ const nextNodeEntry = Editor.next(editor, {
47
+ at: aboveNodeEntry[1]
48
+ });
49
+ Transforms.insertText(editor, text, {
50
+ at: insertPoint
51
+ });
52
+ if (nextNodeEntry) {
53
+ const [nextNode, nextPath] = nextNodeEntry;
54
+ if (Text.isText(nextNode) && nextNode.text === '') {
55
+ Transforms.removeNodes(editor, {
56
+ at: nextPath
57
+ });
58
+ }
59
+ }
60
+ return focusEditor(editor, _objectSpread(_objectSpread({}, insertPoint), {}, {
61
+ offset: insertPoint.offset + text.length
62
+ }));
63
+ }
64
+ }
65
+ }
66
+ return insertText(text);
67
+ };
68
+ newEditor.deleteBackward = unit => {
69
+ const mentionTempInputEntry = getMentionTempIptEntry(editor);
70
+ if (mentionTempInputEntry) {
71
+ const {
72
+ selection
73
+ } = editor;
74
+ if (selection && Range.isCollapsed(selection)) {
75
+ const [node, path] = mentionTempInputEntry;
76
+ const contentString = Node.string(node);
77
+ if (!contentString) {
78
+ return Transforms.delete(editor, {
79
+ at: path
80
+ });
81
+ }
82
+ }
83
+ }
84
+
85
+ // Delete mention, when in mention
86
+ const prevNodeEntry = Editor.previous(editor);
87
+ if (prevNodeEntry) {
88
+ const aboveNodeEntry = Editor.above(editor, {
89
+ match: n => n.type === MENTION,
90
+ at: prevNodeEntry[1]
91
+ });
92
+ const mentionEntry = getMentionEntry(editor);
93
+ if (mentionEntry || aboveNodeEntry) {
94
+ const {
95
+ selection
96
+ } = editor;
97
+ if (selection && Range.isCollapsed(selection)) {
98
+ const [, mentionPath] = mentionEntry || aboveNodeEntry;
99
+ return Transforms.removeNodes(editor, {
100
+ at: mentionPath
101
+ });
102
+ }
103
+ }
104
+ }
105
+ return deleteBackward(unit);
106
+ };
107
+ newEditor.deleteForward = unit => {
108
+ const mentionEntry = Editor.next(editor, {
109
+ match: n => n.type === MENTION
110
+ });
111
+ if (mentionEntry) {
112
+ const [, mentionPath] = mentionEntry;
113
+ return Transforms.removeNodes(editor, {
114
+ at: mentionPath
115
+ });
116
+ }
117
+ return deleteForward(unit);
118
+ };
119
+ newEditor.onHotKeyDown = event => {
120
+ const mentionTempIptEntry = getMentionTempIptEntry(editor);
121
+ if (mentionTempIptEntry) {
122
+ const [, mentionTempIptPath] = mentionTempIptEntry;
123
+ const {
124
+ DownArrow,
125
+ UpArrow,
126
+ Enter,
127
+ Esc,
128
+ RightArrow,
129
+ LeftArrow
130
+ } = KeyCodes;
131
+ const {
132
+ keyCode
133
+ } = event;
134
+ if (keyCode === RightArrow || keyCode === LeftArrow) {
135
+ const {
136
+ selection
137
+ } = editor;
138
+ if (!selection) return;
139
+ if (!Range.isCollapsed(selection)) return;
140
+ if (keyCode === RightArrow) {
141
+ if (Editor.isEnd(editor, selection.focus, mentionTempIptPath)) return transformToText(newEditor);
142
+ }
143
+ if (keyCode === LeftArrow) {
144
+ if (Editor.isStart(editor, selection.focus, mentionTempIptPath)) {
145
+ event.preventDefault();
146
+ return transformToText(newEditor, false);
147
+ }
148
+ }
149
+ }
150
+
151
+ // Handle by collaborators list
152
+ const interceptorKeyCodes = [DownArrow, UpArrow, Enter, Esc];
153
+ if (interceptorKeyCodes.includes(keyCode)) {
154
+ event.preventDefault();
155
+ eventBus.dispatch(INTERNAL_EVENT.HANDLE_MENTION_TEMP_CHOSEN, {
156
+ event
157
+ });
158
+ return;
159
+ }
160
+ }
161
+ const mentionEntry = getMentionEntry(editor);
162
+ if (mentionEntry) {
163
+ const [, mentionPath] = mentionEntry;
164
+ const {
165
+ RightArrow,
166
+ LeftArrow
167
+ } = KeyCodes;
168
+ const {
169
+ keyCode
170
+ } = event;
171
+ if (keyCode === RightArrow || keyCode === LeftArrow) {
172
+ event.preventDefault();
173
+ if (keyCode === LeftArrow) {
174
+ const beginPoint = Editor.start(editor, mentionPath);
175
+ const focusPoint = Editor.before(editor, beginPoint, {
176
+ distance: 1
177
+ });
178
+ focusEditor(newEditor, focusPoint);
179
+ } else {
180
+ const endPoint = Editor.end(editor, mentionPath);
181
+ const focusPoint = Editor.after(editor, endPoint, {
182
+ distance: 1
183
+ });
184
+ focusEditor(newEditor, focusPoint);
185
+ }
186
+ }
187
+ }
188
+ return onHotKeyDown && onHotKeyDown(event);
189
+ };
190
+ newEditor.onCompositionUpdate = event => {
191
+ const mentionTempIptEntry = getMentionTempIptEntry(newEditor);
192
+ if (mentionTempIptEntry) {
193
+ const {
194
+ data
195
+ } = event;
196
+ const compositionText = data.replace(/\'/g, '');
197
+ eventBus.dispatch(INTERNAL_EVENT.UPDATE_MENTION_TEMP_CONTENT, {
198
+ compositionText
199
+ });
200
+ return true;
201
+ }
202
+ };
203
+ newEditor.onCompositionStart = event => {
204
+ const mentionTempIptEntry = getMentionTempIptEntry(editor);
205
+ if (mentionTempIptEntry) {
206
+ event.preventDefault();
207
+ return true;
208
+ }
209
+ };
210
+ newEditor.onCompositionEnd = event => {
211
+ const PrevMentionIptEntry = getPrevMentionIptEntry(newEditor);
212
+ if (PrevMentionIptEntry) {
213
+ const {
214
+ data
215
+ } = event;
216
+ const insertPoint = Editor.end(editor, PrevMentionIptEntry[1]);
217
+ const nextNodeEntry = Editor.next(editor, {
218
+ at: PrevMentionIptEntry[1]
219
+ });
220
+ Transforms.insertText(editor, data, {
221
+ at: insertPoint
222
+ });
223
+ event.preventDefault();
224
+ focusEditor(editor, _objectSpread(_objectSpread({}, insertPoint), {}, {
225
+ offset: insertPoint.offset + data.length
226
+ }));
227
+ if (nextNodeEntry) {
228
+ const [nextNode, nextPath] = nextNodeEntry;
229
+ if (Text.isText(nextNode) && nextNode.text === '') {
230
+ Transforms.removeNodes(editor, {
231
+ at: nextPath
232
+ });
233
+ }
234
+ }
235
+ return true;
236
+ }
237
+ };
238
+ newEditor.isInline = element => {
239
+ if ([MENTION, MENTION_TEMP].includes(element.type)) {
240
+ return true;
241
+ }
242
+ return isInline(element);
243
+ };
244
+ newEditor.normalizeNode = _ref => {
245
+ let [node, path] = _ref;
246
+ const mentionEntry = getMentionEntry(editor);
247
+ if (mentionEntry) {
248
+ const nextEntry = Editor.next(editor, {
249
+ at: mentionEntry[1]
250
+ });
251
+ const focusPoint = Editor.start(editor, nextEntry[1]);
252
+ focusEditor(editor, focusPoint);
253
+ }
254
+ return normalizeNode([node, path]);
255
+ };
256
+ return newEditor;
257
+ };
258
+ export default withMention;
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useEffect, useRef } from 'react';
2
- import { eventStopPropagation } from '../../../utils/mouse-event';
2
+ import { eventStopPropagation } from '../../../../utils/mouse-event';
3
3
  const CommentParticipantItem = _ref => {
4
4
  let {
5
5
  participant,
@@ -5,7 +5,6 @@
5
5
  max-height: 200px;
6
6
  overflow: auto;
7
7
  min-width: 150px;
8
- /* higher than row expand */
9
8
  z-index: 1049;
10
9
  border-radius: 5px;
11
10
  border: 1px solid #ededed;
@@ -43,3 +42,25 @@
43
42
  text-overflow: ellipsis;
44
43
  white-space: nowrap;
45
44
  }
45
+
46
+ .sdoc-mention-temp-ipt {
47
+ padding: 0 5px;
48
+ display: inline-block;
49
+ border-radius: 5px;
50
+ margin: 0 1px;
51
+ }
52
+
53
+ .sdoc-mention {
54
+ display: inline-block;
55
+ margin: 0 2px;
56
+ padding: 0 2px;
57
+ border: none;
58
+ background-color: transparent;
59
+ color: #1677ff;
60
+ border-radius: 5px;
61
+ cursor: pointer;
62
+ }
63
+
64
+ .sdoc-mention:hover {
65
+ background-color: rgb(221, 236, 253);
66
+ }
@@ -0,0 +1,51 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import React, { useCallback, useEffect, useState } from 'react';
3
+ import { Node } from '@seafile/slate';
4
+ import ParticipantPopover from './participant-popover';
5
+ import EventBus from '../../../../utils/event-bus';
6
+ import { INTERNAL_EVENT } from '../../../../constants';
7
+ import './index.css';
8
+ const renderMention = _ref => {
9
+ let {
10
+ attributes,
11
+ children,
12
+ element,
13
+ editor,
14
+ readonly
15
+ } = _ref;
16
+ return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes, {
17
+ contentEditable: "false",
18
+ key: element.id
19
+ }), /*#__PURE__*/React.createElement("button", {
20
+ className: "sdoc-mention"
21
+ }, children));
22
+ };
23
+ const renderMentionTemporaryInput = (_ref2, editor) => {
24
+ let {
25
+ attributes,
26
+ children,
27
+ element,
28
+ readonly
29
+ } = _ref2;
30
+ const [searchText, setSearchText] = useState('');
31
+ const updateSearchText = useCallback(_ref3 => {
32
+ let {
33
+ compositionText
34
+ } = _ref3;
35
+ setSearchText(Node.string(element) + compositionText);
36
+ }, [element]);
37
+ useEffect(() => {
38
+ setSearchText(Node.string(element));
39
+ }, [element]);
40
+ useEffect(() => {
41
+ const eventBus = EventBus.getInstance();
42
+ eventBus.subscribe(INTERNAL_EVENT.UPDATE_MENTION_TEMP_CONTENT, updateSearchText);
43
+ }, [updateSearchText]);
44
+ return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes, {
45
+ className: "sdoc-mention-temp-ipt"
46
+ }), /*#__PURE__*/React.createElement("span", null, "@"), /*#__PURE__*/React.createElement("span", null, children), /*#__PURE__*/React.createElement(ParticipantPopover, {
47
+ searchText: searchText,
48
+ editor: editor
49
+ }));
50
+ };
51
+ export { renderMention, renderMentionTemporaryInput };
@@ -0,0 +1,219 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { Editor, Transforms } from '@seafile/slate';
3
+ import EventBus from '../../../../utils/event-bus';
4
+ import { focusEditor } from '../../../core';
5
+ import { useCollaborators } from '../../../../../hooks';
6
+ import { useParticipantsContext } from '../../../../comment/hooks/use-participants';
7
+ import { searchCollaborators } from '../../../../comment/utils';
8
+ import { INTERNAL_EVENT } from '../../../../constants';
9
+ import { DOWN, FONT_SIZE_WIDTH, LINE_HEIGHT, POPOVER_ADDING_HEIGHT, UP } from '../../../../comment/constants';
10
+ import { eventStopPropagation } from '../../../../utils/mouse-event';
11
+ import { getSelectionCoords } from '../../../../../utils';
12
+ import { KeyCodes } from '../../../../../constants';
13
+ import CommentParticipantItem from './comment-participant-item';
14
+ import { getMentionTempIptEntry, insertMention, sortCollaborators, transformToText } from '../helper';
15
+ import { ElementPopover } from '../../../commons';
16
+ import './index.css';
17
+ const ParticipantPopover = _ref => {
18
+ let {
19
+ editor,
20
+ searchText
21
+ } = _ref;
22
+ const collaboratorsPopoverRef = useRef(null);
23
+ const {
24
+ collaborators
25
+ } = useCollaborators();
26
+ const {
27
+ addParticipants,
28
+ participants
29
+ } = useParticipantsContext();
30
+ const [searchedCollaborators, setSearchedCollaborators] = useState([]);
31
+ const [activeCollaboratorIndex, setActiveCollaboratorIndex] = useState(-1);
32
+ const [validCollaborators, setValidCollaborators] = useState([]);
33
+ useEffect(() => {
34
+ const sortedCollaborators = sortCollaborators(collaborators, participants);
35
+ setValidCollaborators(sortedCollaborators);
36
+ }, [collaborators, participants]);
37
+ useEffect(() => {
38
+ return () => {
39
+ transformToText(editor);
40
+ };
41
+ }, [editor]);
42
+ const hideCommentPopover = useCallback(() => {
43
+ if (searchedCollaborators.length === 0) return;
44
+ setSearchedCollaborators([]);
45
+ setActiveCollaboratorIndex(-1);
46
+ }, [searchedCollaborators]);
47
+ const handleForceClickPopover = useCallback(event => {
48
+ var _collaboratorsPopover;
49
+ if (!((_collaboratorsPopover = collaboratorsPopoverRef.current) === null || _collaboratorsPopover === void 0 ? void 0 : _collaboratorsPopover.contains(event.target))) {
50
+ transformToText(editor);
51
+ }
52
+ }, [editor]);
53
+
54
+ // onMount: handleCommentPopover
55
+ useEffect(() => {
56
+ document.addEventListener('mousedown', handleForceClickPopover);
57
+ return () => {
58
+ document.removeEventListener('mousedown', handleForceClickPopover);
59
+ };
60
+ }, [handleForceClickPopover]);
61
+ const setScrollTop = useCallback((offsetTop, itemOffsetHeight, mouseDownType) => {
62
+ const {
63
+ offsetHeight,
64
+ scrollTop
65
+ } = collaboratorsPopoverRef.current;
66
+ if (mouseDownType === DOWN) {
67
+ if (offsetTop + itemOffsetHeight - scrollTop - offsetHeight + POPOVER_ADDING_HEIGHT > 0) {
68
+ let top = offsetTop + itemOffsetHeight - offsetHeight + POPOVER_ADDING_HEIGHT;
69
+ collaboratorsPopoverRef.current.scrollTop = top;
70
+ }
71
+ }
72
+ if (mouseDownType === UP) {
73
+ if (offsetTop < scrollTop) {
74
+ collaboratorsPopoverRef.current.scrollTop = offsetTop - POPOVER_ADDING_HEIGHT;
75
+ }
76
+ }
77
+ }, []);
78
+ const setCollaboratorsPopoverPosition = useCallback(caretPosition => {
79
+ if (!collaboratorsPopoverRef.current) return;
80
+ const {
81
+ height,
82
+ width
83
+ } = collaboratorsPopoverRef.current.getBoundingClientRect();
84
+ const {
85
+ offsetHeight
86
+ } = collaboratorsPopoverRef.current;
87
+
88
+ // Whether the vertical direction exceeds the screen
89
+ const isVerticalDirectionBeyondScreen = height + caretPosition.y + LINE_HEIGHT > window.innerHeight;
90
+
91
+ // if the vertical direction exceeds the screen, collaboratorsPopoverRef appear above the cursor
92
+ const top = isVerticalDirectionBeyondScreen ? "".concat(caretPosition.y - offsetHeight + LINE_HEIGHT, "px") : "".concat(caretPosition.y + LINE_HEIGHT, "px");
93
+ collaboratorsPopoverRef.current.style.top = top;
94
+
95
+ // Whether the horizontal direction exceeds the screen
96
+ const isHorizontalDirectionBeyondScreen = caretPosition.x + FONT_SIZE_WIDTH + width > window.innerWidth;
97
+
98
+ // if the horizontal direction exceeds the screen, collaboratorsPopoverRef is displayed against the right side of the screen
99
+ const left = isHorizontalDirectionBeyondScreen ? "".concat(window.innerWidth - width, "px") : "".concat(caretPosition.x + FONT_SIZE_WIDTH, "px");
100
+ collaboratorsPopoverRef.current.style.left = left;
101
+ }, [collaboratorsPopoverRef]);
102
+ const getSearchedCollaborators = useCallback(searchingText => {
103
+ if (!searchingText.length) return validCollaborators;
104
+ if (searchingText) return searchCollaborators(validCollaborators, searchingText);
105
+ return [];
106
+ }, [validCollaborators]);
107
+ const handleInvolvedKeyUp = useCallback(() => {
108
+ const searchedCollaborators = getSearchedCollaborators(searchText);
109
+ if (searchedCollaborators.length === 0) {
110
+ hideCommentPopover();
111
+ return;
112
+ }
113
+ setActiveCollaboratorIndex(0);
114
+ setSearchedCollaborators(searchedCollaborators);
115
+ setTimeout(() => {
116
+ const caretPosition = getSelectionCoords();
117
+ setCollaboratorsPopoverPosition(caretPosition);
118
+ }, 1);
119
+ }, [getSearchedCollaborators, searchText, hideCommentPopover, setCollaboratorsPopoverPosition]);
120
+ const handleSelectingCollaborator = useCallback((event, direction) => {
121
+ eventStopPropagation(event);
122
+ const collaboratorsLen = searchedCollaborators.length;
123
+ if (collaboratorsLen === 0) return;
124
+ let nextActiveCollaboratorIndex = activeCollaboratorIndex;
125
+ if (direction === DOWN) {
126
+ nextActiveCollaboratorIndex++;
127
+ if (nextActiveCollaboratorIndex >= collaboratorsLen) {
128
+ nextActiveCollaboratorIndex = 0;
129
+ }
130
+ } else {
131
+ nextActiveCollaboratorIndex--;
132
+ if (nextActiveCollaboratorIndex < 0) {
133
+ nextActiveCollaboratorIndex = collaboratorsLen - 1;
134
+ }
135
+ }
136
+ setActiveCollaboratorIndex(nextActiveCollaboratorIndex);
137
+ }, [searchedCollaborators, activeCollaboratorIndex]);
138
+ const onSelectCollaborator = useCallback(collaborator => {
139
+ const [node, path] = getMentionTempIptEntry(editor);
140
+ insertMention(editor, collaborator);
141
+ addParticipants(collaborator.username);
142
+ Transforms.removeNodes(editor, {
143
+ at: path
144
+ });
145
+ const focusPath = Editor.next(editor, {
146
+ at: path
147
+ })[1];
148
+ focusEditor(editor, Editor.start(editor, focusPath));
149
+ hideCommentPopover();
150
+ }, [editor, hideCommentPopover, addParticipants]);
151
+ const handleSelectCollaborator = useCallback(event => {
152
+ if (searchedCollaborators.length === 0) return;
153
+ onSelectCollaborator(searchedCollaborators[activeCollaboratorIndex]);
154
+ }, [searchedCollaborators, activeCollaboratorIndex, onSelectCollaborator]);
155
+ const handleMentionChosen = useCallback(_ref2 => {
156
+ let {
157
+ event
158
+ } = _ref2;
159
+ if (event.keyCode === KeyCodes.DownArrow) {
160
+ handleSelectingCollaborator(event, DOWN);
161
+ return;
162
+ }
163
+ if (event.keyCode === KeyCodes.UpArrow) {
164
+ handleSelectingCollaborator(event, UP);
165
+ return;
166
+ }
167
+ if (event.keyCode === KeyCodes.Enter) {
168
+ if (collaboratorsPopoverRef.current) {
169
+ handleSelectCollaborator();
170
+ event.preventDefault();
171
+ } else {
172
+ transformToText(editor);
173
+ }
174
+ return;
175
+ }
176
+ if (event.keyCode === KeyCodes.Esc) {
177
+ transformToText(editor);
178
+ return;
179
+ }
180
+ handleInvolvedKeyUp(event);
181
+ }, [editor, handleInvolvedKeyUp, handleSelectCollaborator, handleSelectingCollaborator]);
182
+ const handleSearchTextChange = useCallback(() => {
183
+ const newCollaborators = getSearchedCollaborators(searchText);
184
+ if (newCollaborators.length === 0) {
185
+ hideCommentPopover();
186
+ return;
187
+ }
188
+ setSearchedCollaborators(newCollaborators);
189
+ setTimeout(() => {
190
+ const caretPosition = getSelectionCoords();
191
+ setCollaboratorsPopoverPosition(caretPosition);
192
+ }, 1);
193
+ }, [getSearchedCollaborators, hideCommentPopover, searchText, setCollaboratorsPopoverPosition]);
194
+ useEffect(() => {
195
+ handleSearchTextChange();
196
+ setActiveCollaboratorIndex(0);
197
+ // eslint-disable-next-line react-hooks/exhaustive-deps
198
+ }, [searchText, validCollaborators]);
199
+ useEffect(() => {
200
+ const eventBus = EventBus.getInstance();
201
+ const unsubscribe = eventBus.subscribe(INTERNAL_EVENT.HANDLE_MENTION_TEMP_CHOSEN, handleMentionChosen);
202
+ return () => {
203
+ unsubscribe();
204
+ };
205
+ }, [handleMentionChosen, searchText, validCollaborators]);
206
+ if (searchedCollaborators.length === 0) return null;
207
+ return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
208
+ className: "sdoc-comment-caret-list",
209
+ ref: collaboratorsPopoverRef
210
+ }, searchedCollaborators.map((participant, index) => /*#__PURE__*/React.createElement(CommentParticipantItem, {
211
+ key: participant.username,
212
+ participantIndex: index,
213
+ activeParticipantIndex: activeCollaboratorIndex,
214
+ participant: participant,
215
+ setScrollTop: setScrollTop,
216
+ onSelectParticipant: onSelectCollaborator
217
+ }))));
218
+ };
219
+ export default ParticipantPopover;
@@ -0,0 +1,10 @@
1
+ import { Node, Text } from '@seafile/slate';
2
+ export const isEmptyNode = node => {
3
+ const nodeChildren = node.children;
4
+ const isSingleChild = nodeChildren.length === 1;
5
+ const firstChild = nodeChildren[0];
6
+ const isText = Text.isText(firstChild);
7
+ const isEmptyContent = Node.string(firstChild) === '';
8
+ let isEmpty = isSingleChild && isText && isEmptyContent;
9
+ return isEmpty;
10
+ };
@@ -3,6 +3,8 @@ import React from 'react';
3
3
  import { Node } from '@seafile/slate';
4
4
  import { useSlateStatic } from '@seafile/slate-react';
5
5
  import { Placeholder } from '../../core';
6
+ import { isEmptyNode } from './helper';
7
+ const PLACEHOLDER = 'Please_enter_text';
6
8
  const Paragraph = _ref => {
7
9
  let {
8
10
  isComposing,
@@ -11,12 +13,16 @@ const Paragraph = _ref => {
11
13
  children
12
14
  } = _ref;
13
15
  const {
14
- indent
16
+ indent,
17
+ placeholder = PLACEHOLDER
15
18
  } = element;
16
19
  const editor = useSlateStatic();
17
20
  let isShowPlaceHolder = false;
18
21
  if (editor.children.length === 1) {
19
- isShowPlaceHolder = Node.string(element) === '' && !isComposing;
22
+ const node = editor.children[0];
23
+ const isChildEmpty = isEmptyNode(node);
24
+ const isParagraphEmpty = Node.string(element) === '';
25
+ isShowPlaceHolder = isChildEmpty && isParagraphEmpty && !isComposing;
20
26
  }
21
27
  if (editor.children.length === 2 && editor.children[0].type.startsWith('header')) {
22
28
  const node = editor.children[1];
@@ -33,7 +39,7 @@ const Paragraph = _ref => {
33
39
  position: isShowPlaceHolder ? 'relative' : ''
34
40
  }, style)
35
41
  }), children, isShowPlaceHolder && /*#__PURE__*/React.createElement(Placeholder, {
36
- title: 'Please_enter_text'
42
+ title: placeholder
37
43
  }));
38
44
  };
39
45
  export const renderParagraph = props => {
@@ -0,0 +1,68 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import React, { useCallback } from 'react';
3
+ import { withTranslation } from 'react-i18next';
4
+ import { TEXT_STYLE, MENUS_CONFIG_MAP } from '../../../constants';
5
+ import { focusEditor } from '../../../core';
6
+ import { MenuItem } from '../../../commons';
7
+ import { getValue, isMenuDisabled, addMark, removeMark } from '../helpers';
8
+ import { BOLD, ITALIC } from '../../../constants/menus-config';
9
+ const filterFontTypes = _ref => {
10
+ let {
11
+ id
12
+ } = _ref;
13
+ return [BOLD, ITALIC].includes(id);
14
+ };
15
+ const CommentEditorTextStyleMenuList = _ref2 => {
16
+ let {
17
+ editor,
18
+ isRichEditor,
19
+ className,
20
+ idPrefix,
21
+ readonly
22
+ } = _ref2;
23
+ const isActive = useCallback(type => {
24
+ const isMark = getValue(editor, type);
25
+ return !!isMark;
26
+
27
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28
+ }, [editor]);
29
+ const isDisabled = useCallback(() => {
30
+ return isMenuDisabled(editor, readonly);
31
+
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ }, [editor, readonly]);
34
+ const onMouseDown = useCallback((event, type) => {
35
+ event.preventDefault();
36
+ event.stopPropagation();
37
+ if (isDisabled()) return;
38
+ const active = isActive(type);
39
+ if (active) {
40
+ removeMark(editor, type);
41
+ } else {
42
+ addMark(editor, type);
43
+ }
44
+ focusEditor(editor);
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ }, [editor]);
47
+ const getTextStyleList = useCallback(key => {
48
+ return MENUS_CONFIG_MAP[key].map(item => {
49
+ let itemProps = {
50
+ isRichEditor,
51
+ className,
52
+ disabled: isDisabled(),
53
+ isActive: isActive(item.type),
54
+ onMouseDown: onMouseDown
55
+ };
56
+ return _objectSpread(_objectSpread(_objectSpread({}, itemProps), item), {}, {
57
+ id: idPrefix ? "".concat(idPrefix, "_").concat(item.id) : item.id
58
+ });
59
+ });
60
+
61
+ // eslint-disable-next-line react-hooks/exhaustive-deps
62
+ }, [editor, readonly]);
63
+ const list = getTextStyleList(TEXT_STYLE).filter(filterFontTypes);
64
+ return /*#__PURE__*/React.createElement(React.Fragment, null, list.map((itemProps, index) => /*#__PURE__*/React.createElement(MenuItem, Object.assign({
65
+ key: index
66
+ }, itemProps))));
67
+ };
68
+ export default withTranslation('sdoc-editor')(CommentEditorTextStyleMenuList);