@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.
- package/dist/assets/css/simple-editor.css +15 -0
- package/dist/basic-sdk/assets/css/layout.css +15 -0
- package/dist/basic-sdk/comment/components/comment-editor.js +49 -33
- package/dist/basic-sdk/comment/components/comment-item-content.js +11 -3
- package/dist/basic-sdk/comment/components/comment-item-reply.js +13 -5
- package/dist/basic-sdk/comment/components/comment-item-wrapper.js +1 -1
- package/dist/basic-sdk/comment/components/comment-list.css +27 -19
- package/dist/basic-sdk/comment/components/comment-list.js +0 -1
- package/dist/basic-sdk/comment/components/global-comment/index.css +2 -6
- package/dist/basic-sdk/comment/utils/index.js +5 -5
- package/dist/basic-sdk/constants/index.js +5 -2
- package/dist/basic-sdk/editor/comment-article.js +175 -0
- package/dist/basic-sdk/editor/editable-article.js +2 -1
- package/dist/basic-sdk/editor/sdoc-comment-editor.js +108 -0
- package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +12 -7
- package/dist/basic-sdk/extension/commons/menu/menu-item.js +1 -1
- package/dist/basic-sdk/extension/constants/element-type.js +3 -1
- package/dist/basic-sdk/extension/constants/index.js +2 -2
- package/dist/basic-sdk/extension/constants/menus-config.js +2 -2
- package/dist/basic-sdk/extension/index.js +11 -1
- package/dist/basic-sdk/extension/plugins/blockquote/plugin.js +3 -3
- package/dist/basic-sdk/extension/plugins/callout/helper.js +5 -2
- package/dist/basic-sdk/extension/plugins/image/helpers.js +6 -3
- package/dist/basic-sdk/extension/plugins/image/menu/index.js +25 -4
- package/dist/basic-sdk/extension/plugins/index.js +3 -1
- package/dist/basic-sdk/extension/plugins/link/menu/index.js +22 -3
- package/dist/basic-sdk/extension/plugins/markdown/plugin.js +17 -6
- package/dist/basic-sdk/extension/plugins/mention/helper.js +142 -0
- package/dist/basic-sdk/extension/plugins/mention/index.js +10 -0
- package/dist/basic-sdk/extension/plugins/mention/plugin.js +258 -0
- package/dist/basic-sdk/{comment/components/comment-input → extension/plugins/mention/render-elem}/comment-participant-item.js +1 -1
- package/dist/basic-sdk/{comment/components/comment-input → extension/plugins/mention/render-elem}/index.css +22 -1
- package/dist/basic-sdk/extension/plugins/mention/render-elem/index.js +51 -0
- package/dist/basic-sdk/extension/plugins/mention/render-elem/participant-popover.js +219 -0
- package/dist/basic-sdk/extension/plugins/paragraph/helper.js +10 -0
- package/dist/basic-sdk/extension/plugins/paragraph/render-elem.js +9 -3
- package/dist/basic-sdk/extension/plugins/text-style/menu/comemnt-editor-menu.js +68 -0
- package/dist/basic-sdk/extension/plugins/text-style/menu/index.js +19 -8
- package/dist/basic-sdk/extension/render/custom-element.js +12 -2
- package/dist/basic-sdk/extension/toolbar/comment-editor-toolbar/index.js +45 -0
- package/dist/basic-sdk/utils/diff.js +13 -0
- package/dist/basic-sdk/utils/event-handler.js +21 -0
- package/dist/components/doc-operations/revision-operations/changes-count/index.js +19 -9
- package/dist/slate-convert/md-to-slate/transform.js +17 -0
- package/dist/slate-convert/slate-to-md/transform.js +28 -2
- package/package.json +1 -1
- package/dist/basic-sdk/comment/components/comment-input/helpers.js +0 -15
- package/dist/basic-sdk/comment/components/comment-input/index.js +0 -306
|
@@ -10,7 +10,15 @@ import FontSizeScale from '../../font/menu/font-size/font-size-scale';
|
|
|
10
10
|
import { getValue, isMenuDisabled, addMark, removeMark } from '../helpers';
|
|
11
11
|
import { useColorContext } from '../../../../hooks/use-color-context';
|
|
12
12
|
import { eventStopPropagation } from '../../../../utils/mouse-event';
|
|
13
|
-
|
|
13
|
+
import { BOLD, ITALIC } from '../../../constants/menus-config';
|
|
14
|
+
import { COMMENT_EDITOR } from '../../../../constants';
|
|
15
|
+
const filterFontTypes = _ref => {
|
|
16
|
+
let {
|
|
17
|
+
id
|
|
18
|
+
} = _ref;
|
|
19
|
+
return [BOLD, ITALIC].includes(id);
|
|
20
|
+
};
|
|
21
|
+
const TextStyleMenuList = _ref2 => {
|
|
14
22
|
let {
|
|
15
23
|
editor,
|
|
16
24
|
t,
|
|
@@ -18,9 +26,10 @@ const TextStyleMenuList = _ref => {
|
|
|
18
26
|
className,
|
|
19
27
|
idPrefix,
|
|
20
28
|
readonly
|
|
21
|
-
} =
|
|
29
|
+
} = _ref2;
|
|
22
30
|
let selectedFontSize = getFontSize(editor);
|
|
23
31
|
let selectedFontSizeValue = selectedFontSize;
|
|
32
|
+
const isCommentEditor = editor.editorType === COMMENT_EDITOR;
|
|
24
33
|
const {
|
|
25
34
|
lastUsedFontColor,
|
|
26
35
|
updateLastUsedFontColor,
|
|
@@ -94,18 +103,20 @@ const TextStyleMenuList = _ref => {
|
|
|
94
103
|
|
|
95
104
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
96
105
|
}, [editor, lastUsedFontColor, lastUsedHighlightColor, readonly]);
|
|
97
|
-
|
|
106
|
+
let list = getTextStyleList(TEXT_STYLE);
|
|
107
|
+
// Filter for comment editor
|
|
108
|
+
if (isCommentEditor) {
|
|
109
|
+
list = list.filter(filterFontTypes);
|
|
110
|
+
}
|
|
98
111
|
const dropdownList = getTextStyleList(TEXT_STYLE_MORE);
|
|
99
112
|
return /*#__PURE__*/React.createElement(React.Fragment, null, list.map((itemProps, index) => {
|
|
100
113
|
const Component = itemProps.isColor ? ColorMenu : MenuItem;
|
|
101
114
|
return /*#__PURE__*/React.createElement(Component, Object.assign({
|
|
102
115
|
key: index
|
|
103
116
|
}, itemProps));
|
|
104
|
-
}), /*#__PURE__*/React.createElement(MoreDropdown, null, dropdownList.map((itemProps, index) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}, itemProps));
|
|
108
|
-
}), /*#__PURE__*/React.createElement(FontSizeScale, {
|
|
117
|
+
}), !isCommentEditor && /*#__PURE__*/React.createElement(MoreDropdown, null, dropdownList.map((itemProps, index) => /*#__PURE__*/React.createElement(MenuItem, Object.assign({
|
|
118
|
+
key: index
|
|
119
|
+
}, itemProps))), /*#__PURE__*/React.createElement(FontSizeScale, {
|
|
109
120
|
disabled: isDisabled(),
|
|
110
121
|
onClick: increaseFontSize,
|
|
111
122
|
id: "sdoc-increase-font-size",
|
|
@@ -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 } from '../constants';
|
|
4
|
-
import { BlockquotePlugin, LinkPlugin, CheckListPlugin, HeaderPlugin, ListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin } 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 } from '../constants';
|
|
4
|
+
import { BlockquotePlugin, LinkPlugin, CheckListPlugin, HeaderPlugin, ListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, MentionPlugin } from '../plugins';
|
|
5
5
|
import { onDragOver, onDragLeave, onDrop } from '../toolbar/side-toolbar/event';
|
|
6
6
|
import { getParentNode } from '../core';
|
|
7
7
|
import { setMouseEnter } from './helper';
|
|
@@ -136,6 +136,16 @@ const CustomRenderElement = props => {
|
|
|
136
136
|
const [renderCallout] = CalloutPlugin.renderElements;
|
|
137
137
|
return renderCallout(props, editor);
|
|
138
138
|
}
|
|
139
|
+
case MENTION:
|
|
140
|
+
{
|
|
141
|
+
const [renderMention] = MentionPlugin.renderElements;
|
|
142
|
+
return renderMention(props, editor);
|
|
143
|
+
}
|
|
144
|
+
case MENTION_TEMP:
|
|
145
|
+
{
|
|
146
|
+
const [, renderMentionTemporaryInput] = MentionPlugin.renderElements;
|
|
147
|
+
return renderMentionTemporaryInput(props, editor);
|
|
148
|
+
}
|
|
139
149
|
default:
|
|
140
150
|
{
|
|
141
151
|
const [renderParagraph] = ParagraphPlugin.renderElements;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import useSelectionUpdate from '../../../hooks/use-selection-update';
|
|
3
|
+
import { ORDERED_LIST, UNORDERED_LIST } from '../../constants';
|
|
4
|
+
import { MenuGroup } from '../../commons';
|
|
5
|
+
import ListMenu from '../../plugins/list/menu';
|
|
6
|
+
import CommentEditorTextStyleMenuList from '../../plugins/text-style/menu/comemnt-editor-menu';
|
|
7
|
+
import ImageMenu from '../../plugins/image/menu';
|
|
8
|
+
import EventBus from '../../../utils/event-bus';
|
|
9
|
+
import LinkMenu from '../../plugins/link/menu';
|
|
10
|
+
const CommentEditorToolbar = _ref => {
|
|
11
|
+
let {
|
|
12
|
+
editor,
|
|
13
|
+
readonly
|
|
14
|
+
} = _ref;
|
|
15
|
+
useSelectionUpdate();
|
|
16
|
+
const eventBus = EventBus.getInstance();
|
|
17
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
18
|
+
className: "sdoc-comment-editor-toolbar"
|
|
19
|
+
}, /*#__PURE__*/React.createElement(MenuGroup, {
|
|
20
|
+
className: "menu-group sdoc-comment-editor-menu-group"
|
|
21
|
+
}, /*#__PURE__*/React.createElement(CommentEditorTextStyleMenuList, {
|
|
22
|
+
editor: editor,
|
|
23
|
+
readonly: readonly
|
|
24
|
+
}), /*#__PURE__*/React.createElement(ListMenu, {
|
|
25
|
+
editor: editor,
|
|
26
|
+
type: UNORDERED_LIST,
|
|
27
|
+
readonly: readonly
|
|
28
|
+
}), /*#__PURE__*/React.createElement(ListMenu, {
|
|
29
|
+
editor: editor,
|
|
30
|
+
type: ORDERED_LIST,
|
|
31
|
+
readonly: readonly
|
|
32
|
+
}), /*#__PURE__*/React.createElement(LinkMenu, {
|
|
33
|
+
editor: editor,
|
|
34
|
+
readonly: readonly,
|
|
35
|
+
eventBus: eventBus
|
|
36
|
+
}), /*#__PURE__*/React.createElement(ImageMenu, {
|
|
37
|
+
editor: editor,
|
|
38
|
+
readonly: readonly,
|
|
39
|
+
eventBus: eventBus
|
|
40
|
+
})));
|
|
41
|
+
};
|
|
42
|
+
CommentEditorToolbar.defaultProps = {
|
|
43
|
+
readonly: false
|
|
44
|
+
};
|
|
45
|
+
export default CommentEditorToolbar;
|
|
@@ -17,6 +17,19 @@ const generatorDiffTextElement = function (textElement, diffType) {
|
|
|
17
17
|
[diffType]: true
|
|
18
18
|
}, style);
|
|
19
19
|
};
|
|
20
|
+
export const getTopLevelChanges = changes => {
|
|
21
|
+
const topLevelChanges = [];
|
|
22
|
+
changes.forEach(item => {
|
|
23
|
+
let dom = document.querySelectorAll("[data-id=".concat(item, "]"))[0];
|
|
24
|
+
while (((_dom = dom) === null || _dom === void 0 ? void 0 : (_dom$dataset = _dom.dataset) === null || _dom$dataset === void 0 ? void 0 : _dom$dataset.root) !== 'true') {
|
|
25
|
+
var _dom, _dom$dataset, _dom2;
|
|
26
|
+
if (!((_dom2 = dom) === null || _dom2 === void 0 ? void 0 : _dom2.parentNode)) break;
|
|
27
|
+
dom = dom.parentNode;
|
|
28
|
+
}
|
|
29
|
+
topLevelChanges.push(dom.dataset.id);
|
|
30
|
+
});
|
|
31
|
+
return Array.from(new Set(topLevelChanges));
|
|
32
|
+
};
|
|
20
33
|
|
|
21
34
|
// Depth facilitates each child node, adding diffType to each end node
|
|
22
35
|
const generatorDiffElement = function (element, diffType) {
|
|
@@ -92,6 +92,27 @@ class EventProxy {
|
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
94
|
_defineProperty(this, "onPaste", event => {});
|
|
95
|
+
_defineProperty(this, "onCompositionStart", event => {
|
|
96
|
+
const editor = this.editor;
|
|
97
|
+
if (editor.onCompositionStart) {
|
|
98
|
+
const isHandled = editor.onCompositionStart(event);
|
|
99
|
+
if (isHandled) return;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
_defineProperty(this, "onCompositionUpdate", event => {
|
|
103
|
+
const editor = this.editor;
|
|
104
|
+
if (editor.onCompositionUpdate) {
|
|
105
|
+
const isHandled = editor.onCompositionUpdate(event);
|
|
106
|
+
if (isHandled) return;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
_defineProperty(this, "onCompositionEnd", event => {
|
|
110
|
+
const editor = this.editor;
|
|
111
|
+
if (editor.onCompositionUpdate) {
|
|
112
|
+
const isHandled = editor.onCompositionEnd(event);
|
|
113
|
+
if (isHandled) return;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
95
116
|
this.editor = _editor;
|
|
96
117
|
}
|
|
97
118
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { useCallback, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import Tooltip from '../../../tooltip';
|
|
4
|
+
import { getTopLevelChanges } from '../../../../basic-sdk/utils/diff';
|
|
4
5
|
import './index.css';
|
|
5
6
|
const ChangesCount = _ref => {
|
|
6
7
|
let {
|
|
@@ -10,9 +11,18 @@ const ChangesCount = _ref => {
|
|
|
10
11
|
t
|
|
11
12
|
} = useTranslation();
|
|
12
13
|
const [currentDiffIndex, setDiffIndex] = useState(0);
|
|
14
|
+
const [topLevelChanges, setTopLevelChanges] = useState([]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (changes.length !== 0) {
|
|
17
|
+
queueMicrotask(() => {
|
|
18
|
+
const topLevelChanges = getTopLevelChanges(changes);
|
|
19
|
+
setTopLevelChanges([...topLevelChanges]);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}, [changes]);
|
|
13
23
|
const jumpToElement = useCallback(currentDiffIndex => {
|
|
14
24
|
setDiffIndex(currentDiffIndex);
|
|
15
|
-
const change =
|
|
25
|
+
const change = topLevelChanges[currentDiffIndex];
|
|
16
26
|
const changeElement = document.querySelectorAll("[data-id=".concat(change, "]"))[0];
|
|
17
27
|
if (changeElement) {
|
|
18
28
|
const scrollContainer = document.getElementById('sdoc-scroll-container');
|
|
@@ -22,31 +32,31 @@ const ChangesCount = _ref => {
|
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
|
-
}, [
|
|
35
|
+
}, [topLevelChanges, currentDiffIndex]);
|
|
26
36
|
const lastChange = useCallback(() => {
|
|
27
37
|
if (currentDiffIndex === 0) {
|
|
28
|
-
jumpToElement(
|
|
38
|
+
jumpToElement(topLevelChanges.length - 1);
|
|
29
39
|
return;
|
|
30
40
|
}
|
|
31
41
|
jumpToElement(currentDiffIndex - 1);
|
|
32
42
|
|
|
33
43
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
|
-
}, [
|
|
44
|
+
}, [topLevelChanges, currentDiffIndex]);
|
|
35
45
|
const nextChange = useCallback(() => {
|
|
36
|
-
if (currentDiffIndex ===
|
|
46
|
+
if (currentDiffIndex === topLevelChanges.length - 1) {
|
|
37
47
|
jumpToElement(0);
|
|
38
48
|
return;
|
|
39
49
|
}
|
|
40
50
|
jumpToElement(currentDiffIndex + 1);
|
|
41
51
|
|
|
42
52
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
|
-
}, [
|
|
44
|
-
if (!Array.isArray(
|
|
53
|
+
}, [topLevelChanges, currentDiffIndex]);
|
|
54
|
+
if (!Array.isArray(topLevelChanges) || topLevelChanges.length === 0) {
|
|
45
55
|
return /*#__PURE__*/React.createElement("div", {
|
|
46
56
|
className: "sdoc-revision-changes-container d-flex align-items-center pl-2 pr-2 ml-4"
|
|
47
57
|
}, t('No_changes'));
|
|
48
58
|
}
|
|
49
|
-
const changesCount =
|
|
59
|
+
const changesCount = topLevelChanges.length;
|
|
50
60
|
return /*#__PURE__*/React.createElement("div", {
|
|
51
61
|
className: "sdoc-revision-changes-container d-flex align-items-center ml-4"
|
|
52
62
|
}, /*#__PURE__*/React.createElement("div", {
|
|
@@ -17,10 +17,27 @@ const applyMarkForInlineItem = function (result, item) {
|
|
|
17
17
|
value
|
|
18
18
|
} = item;
|
|
19
19
|
if (type === LINK) {
|
|
20
|
+
var _item$title, _item$title$startsWit;
|
|
20
21
|
const child = children.length === 0 ? {
|
|
21
22
|
type: 'text',
|
|
22
23
|
value: ''
|
|
23
24
|
} : children[0];
|
|
25
|
+
|
|
26
|
+
// Mention node
|
|
27
|
+
if ((_item$title = item.title) === null || _item$title === void 0 ? void 0 : (_item$title$startsWit = _item$title.startsWith) === null || _item$title$startsWit === void 0 ? void 0 : _item$title$startsWit.call(_item$title, '__sdoc_mention__username')) {
|
|
28
|
+
const username = item.title.split('__sdoc_mention__username')[1];
|
|
29
|
+
const mention = {
|
|
30
|
+
id: slugid.nice(),
|
|
31
|
+
username,
|
|
32
|
+
type: 'mention',
|
|
33
|
+
children: [{
|
|
34
|
+
id: slugid.nice(),
|
|
35
|
+
text: child.value
|
|
36
|
+
}]
|
|
37
|
+
};
|
|
38
|
+
result.push(mention);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
24
41
|
const linkChildren = [{
|
|
25
42
|
id: slugid.nice(),
|
|
26
43
|
text: child.value
|
|
@@ -99,13 +99,31 @@ const transformInlineChildren = (result, item) => {
|
|
|
99
99
|
if (item.type && item.type === 'link') {
|
|
100
100
|
const link = {
|
|
101
101
|
type: 'link',
|
|
102
|
-
url: item.
|
|
102
|
+
url: item.href,
|
|
103
103
|
title: item.title || null,
|
|
104
104
|
children: [transformTextNode(item.children[0])]
|
|
105
105
|
};
|
|
106
106
|
result.push(link);
|
|
107
107
|
return result;
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
// mention
|
|
111
|
+
if (item.type && item.type === 'mention') {
|
|
112
|
+
const text = item.children[0].text;
|
|
113
|
+
const username = item.username;
|
|
114
|
+
const mention = {
|
|
115
|
+
type: 'link',
|
|
116
|
+
// eslint-disable-next-line no-script-url
|
|
117
|
+
url: 'javascript:void(0)',
|
|
118
|
+
title: "__sdoc_mention__username".concat(username),
|
|
119
|
+
children: [{
|
|
120
|
+
type: 'text',
|
|
121
|
+
value: text
|
|
122
|
+
}]
|
|
123
|
+
};
|
|
124
|
+
result.push(mention);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
109
127
|
if (item.type && item.type === 'column') {
|
|
110
128
|
const data = item.data;
|
|
111
129
|
const newNode = {
|
|
@@ -286,6 +304,13 @@ const transformFormula = node => {
|
|
|
286
304
|
value: data.formula
|
|
287
305
|
};
|
|
288
306
|
};
|
|
307
|
+
const transformMention = node => {
|
|
308
|
+
const data = node.data;
|
|
309
|
+
return {
|
|
310
|
+
type: 'mention',
|
|
311
|
+
value: data.value
|
|
312
|
+
};
|
|
313
|
+
};
|
|
289
314
|
const elementHandlers = {
|
|
290
315
|
'paragraph': transformParagraph,
|
|
291
316
|
'header1': transformHeader,
|
|
@@ -300,7 +325,8 @@ const elementHandlers = {
|
|
|
300
325
|
'ordered_list': transformList,
|
|
301
326
|
'unordered_list': transformList,
|
|
302
327
|
'code_block': transformCodeBlock,
|
|
303
|
-
'formula': transformFormula
|
|
328
|
+
'formula': transformFormula,
|
|
329
|
+
'mention': transformMention
|
|
304
330
|
};
|
|
305
331
|
export const formatSlateToMd = children => {
|
|
306
332
|
const validChildren = children.filter(child => elementHandlers[child.type]);
|
package/package.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import context from '../../../../context';
|
|
2
|
-
|
|
3
|
-
// Sort collaborators by participants, move mentioned members to the top
|
|
4
|
-
export const sortCollaborators = function (collaborators) {
|
|
5
|
-
let participants = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
6
|
-
const loginEmail = context.getUserInfo().email;
|
|
7
|
-
const participantsMap = {};
|
|
8
|
-
participants.forEach(item => {
|
|
9
|
-
if (item.email === loginEmail) return;
|
|
10
|
-
participantsMap[item.email] = item;
|
|
11
|
-
});
|
|
12
|
-
const newCollaborators = collaborators.filter(item => !participantsMap[item.email] && item.email !== loginEmail);
|
|
13
|
-
const newParticipants = Object.values(participantsMap);
|
|
14
|
-
return [...newParticipants, ...newCollaborators];
|
|
15
|
-
};
|
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';
|
|
2
|
-
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { useCollaborators } from '../../../../hooks';
|
|
4
|
-
import { useParticipantsContext } from '../../hooks/use-participants';
|
|
5
|
-
import ModalPortal from '../../../../components/modal-portal';
|
|
6
|
-
import { KeyCodes } from '../../../../constants';
|
|
7
|
-
import { searchCollaborators, CommentUtilities, convertComment, checkMentionOperation } from '../../utils';
|
|
8
|
-
import { DOWN, UP, POPOVER_ADDING_HEIGHT, FONT_SIZE_WIDTH, LINE_HEIGHT } from '../../constants';
|
|
9
|
-
import { eventStopPropagation } from '../../../utils/mouse-event';
|
|
10
|
-
import CommentParticipantItem from './comment-participant-item';
|
|
11
|
-
import { Hotkey, getSelectionCoords } from '../../../../utils';
|
|
12
|
-
import { sortCollaborators } from './helpers';
|
|
13
|
-
import './index.css';
|
|
14
|
-
const CommentInput = forwardRef((_ref, ref) => {
|
|
15
|
-
let {
|
|
16
|
-
placeholder,
|
|
17
|
-
content,
|
|
18
|
-
type,
|
|
19
|
-
onCancel,
|
|
20
|
-
onSubmit
|
|
21
|
-
} = _ref;
|
|
22
|
-
const commentRef = useRef();
|
|
23
|
-
const collaboratorsPopoverRef = useRef();
|
|
24
|
-
const {
|
|
25
|
-
t
|
|
26
|
-
} = useTranslation();
|
|
27
|
-
const {
|
|
28
|
-
collaborators
|
|
29
|
-
} = useCollaborators();
|
|
30
|
-
const {
|
|
31
|
-
addParticipants,
|
|
32
|
-
participants
|
|
33
|
-
} = useParticipantsContext();
|
|
34
|
-
const [range, setRange] = useState();
|
|
35
|
-
const [searchedCollaborators, setSearchedCollaborators] = useState([]);
|
|
36
|
-
const [activeCollaboratorIndex, setActiveCollaboratorIndex] = useState(-1);
|
|
37
|
-
const [validCollaborators, setValidCollaborators] = useState([]);
|
|
38
|
-
const commentUtilities = new CommentUtilities();
|
|
39
|
-
|
|
40
|
-
// onMount: comment content
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (!content) return;
|
|
43
|
-
commentRef.current.textContent = content;
|
|
44
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
|
-
}, []);
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
const sortedCollaborators = sortCollaborators(collaborators, participants);
|
|
48
|
-
setValidCollaborators(sortedCollaborators);
|
|
49
|
-
}, [collaborators, participants]);
|
|
50
|
-
|
|
51
|
-
// onMount: set input focus
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
if (type !== 'comment') return;
|
|
54
|
-
commentRef.current && commentRef.current.focus();
|
|
55
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
56
|
-
}, []);
|
|
57
|
-
|
|
58
|
-
// onMount: handleCommentPopover
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
document.addEventListener('mousedown', handleCommentPopover);
|
|
61
|
-
return () => {
|
|
62
|
-
document.removeEventListener('mousedown', handleCommentPopover);
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// The parent component can call the method of this component through ref
|
|
67
|
-
useImperativeHandle(ref, () => ({
|
|
68
|
-
getValue: () => {
|
|
69
|
-
const comment = convertComment(commentRef.current.textContent);
|
|
70
|
-
return comment;
|
|
71
|
-
},
|
|
72
|
-
setValue: value => {
|
|
73
|
-
commentRef.current.textContent = value || '';
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
77
|
-
}), [commentRef]);
|
|
78
|
-
const hideCommentPopover = useCallback(() => {
|
|
79
|
-
if (searchedCollaborators.length === 0) return;
|
|
80
|
-
setSearchedCollaborators([]);
|
|
81
|
-
setActiveCollaboratorIndex(-1);
|
|
82
|
-
}, [searchedCollaborators]);
|
|
83
|
-
const handleCommentPopover = useCallback(event => {
|
|
84
|
-
if (collaboratorsPopoverRef.current && event && !collaboratorsPopoverRef.current.contains(event.target)) {
|
|
85
|
-
hideCommentPopover();
|
|
86
|
-
}
|
|
87
|
-
}, [hideCommentPopover, collaboratorsPopoverRef]);
|
|
88
|
-
const setScrollTop = useCallback((offsetTop, itemOffsetHeight, mouseDownType) => {
|
|
89
|
-
const {
|
|
90
|
-
offsetHeight,
|
|
91
|
-
scrollTop
|
|
92
|
-
} = collaboratorsPopoverRef.current;
|
|
93
|
-
if (mouseDownType === DOWN) {
|
|
94
|
-
if (offsetTop + itemOffsetHeight - scrollTop - offsetHeight + POPOVER_ADDING_HEIGHT > 0) {
|
|
95
|
-
let top = offsetTop + itemOffsetHeight - offsetHeight + POPOVER_ADDING_HEIGHT;
|
|
96
|
-
collaboratorsPopoverRef.current.scrollTop = top;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (mouseDownType === UP) {
|
|
100
|
-
if (offsetTop < scrollTop) {
|
|
101
|
-
collaboratorsPopoverRef.current.scrollTop = offsetTop - POPOVER_ADDING_HEIGHT;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}, []);
|
|
105
|
-
const setCollaboratorsPopoverPosition = useCallback(caretPosition => {
|
|
106
|
-
const {
|
|
107
|
-
height,
|
|
108
|
-
width
|
|
109
|
-
} = collaboratorsPopoverRef.current.getBoundingClientRect();
|
|
110
|
-
const {
|
|
111
|
-
offsetHeight
|
|
112
|
-
} = collaboratorsPopoverRef.current;
|
|
113
|
-
|
|
114
|
-
// Whether the vertical direction exceeds the screen
|
|
115
|
-
const isVerticalDirectionBeyondScreen = height + caretPosition.y + LINE_HEIGHT > window.innerHeight;
|
|
116
|
-
|
|
117
|
-
// if the vertical direction exceeds the screen, collaboratorsPopoverRef appear above the cursor
|
|
118
|
-
const top = isVerticalDirectionBeyondScreen ? "".concat(caretPosition.y - offsetHeight + LINE_HEIGHT, "px") : "".concat(caretPosition.y + LINE_HEIGHT, "px");
|
|
119
|
-
collaboratorsPopoverRef.current.style.top = top;
|
|
120
|
-
|
|
121
|
-
// Whether the horizontal direction exceeds the screen
|
|
122
|
-
const isHorizontalDirectionBeyondScreen = caretPosition.x + FONT_SIZE_WIDTH + width > window.innerWidth;
|
|
123
|
-
|
|
124
|
-
// if the horizontal direction exceeds the screen, collaboratorsPopoverRef is displayed against the right side of the screen
|
|
125
|
-
const left = isHorizontalDirectionBeyondScreen ? "".concat(window.innerWidth - width, "px") : "".concat(caretPosition.x + FONT_SIZE_WIDTH, "px");
|
|
126
|
-
collaboratorsPopoverRef.current.style.left = left;
|
|
127
|
-
}, [collaboratorsPopoverRef]);
|
|
128
|
-
const getAtInfo = useCallback(event => {
|
|
129
|
-
const isValidOperation = checkMentionOperation(event);
|
|
130
|
-
if (!isValidOperation) return {
|
|
131
|
-
index: -1
|
|
132
|
-
};
|
|
133
|
-
const selection = window.getSelection();
|
|
134
|
-
const {
|
|
135
|
-
isCollapsed,
|
|
136
|
-
anchorNode,
|
|
137
|
-
anchorOffset
|
|
138
|
-
} = selection;
|
|
139
|
-
if (!isCollapsed || !anchorNode || !anchorNode.data) return {
|
|
140
|
-
index: -1
|
|
141
|
-
};
|
|
142
|
-
const text = anchorNode.data;
|
|
143
|
-
const index = commentUtilities.getAtIndexWithAnchorPosition(anchorOffset, text);
|
|
144
|
-
return {
|
|
145
|
-
index,
|
|
146
|
-
text,
|
|
147
|
-
selection
|
|
148
|
-
};
|
|
149
|
-
}, [commentUtilities]);
|
|
150
|
-
const getSearchedCollaborators = useCallback(_ref2 => {
|
|
151
|
-
let {
|
|
152
|
-
atIndex,
|
|
153
|
-
text,
|
|
154
|
-
anchorOffset
|
|
155
|
-
} = _ref2;
|
|
156
|
-
if (atIndex !== 0 && text[atIndex - 1] !== ' ') return [];
|
|
157
|
-
if (atIndex === anchorOffset - 1) return validCollaborators;
|
|
158
|
-
const searchingText = text.substring(atIndex + 1);
|
|
159
|
-
if (searchingText) return searchCollaborators(validCollaborators, searchingText);
|
|
160
|
-
return [];
|
|
161
|
-
}, [validCollaborators]);
|
|
162
|
-
const handleInvolvedKeyUp = useCallback(event => {
|
|
163
|
-
// atIndex: Positional index of @
|
|
164
|
-
const {
|
|
165
|
-
index: atIndex,
|
|
166
|
-
text,
|
|
167
|
-
selection
|
|
168
|
-
} = getAtInfo(event);
|
|
169
|
-
if (atIndex === -1) {
|
|
170
|
-
hideCommentPopover();
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
const {
|
|
174
|
-
anchorOffset
|
|
175
|
-
} = selection;
|
|
176
|
-
const searchedCollaborators = getSearchedCollaborators({
|
|
177
|
-
atIndex,
|
|
178
|
-
text,
|
|
179
|
-
anchorOffset
|
|
180
|
-
});
|
|
181
|
-
if (searchedCollaborators.length === 0) {
|
|
182
|
-
hideCommentPopover();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
setActiveCollaboratorIndex(0);
|
|
186
|
-
setRange(selection.getRangeAt(0));
|
|
187
|
-
setSearchedCollaborators(searchedCollaborators);
|
|
188
|
-
setTimeout(() => {
|
|
189
|
-
const caretPosition = getSelectionCoords();
|
|
190
|
-
setCollaboratorsPopoverPosition(caretPosition);
|
|
191
|
-
}, 1);
|
|
192
|
-
}, [getSearchedCollaborators, getAtInfo, hideCommentPopover, setCollaboratorsPopoverPosition]);
|
|
193
|
-
const handleSelectingCollaborator = useCallback((event, direction) => {
|
|
194
|
-
eventStopPropagation(event);
|
|
195
|
-
const collaboratorsLen = searchedCollaborators.length;
|
|
196
|
-
if (collaboratorsLen === 0) return;
|
|
197
|
-
let nextActiveCollaboratorIndex = activeCollaboratorIndex;
|
|
198
|
-
if (direction === DOWN) {
|
|
199
|
-
nextActiveCollaboratorIndex++;
|
|
200
|
-
if (nextActiveCollaboratorIndex >= collaboratorsLen) {
|
|
201
|
-
nextActiveCollaboratorIndex = 0;
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
nextActiveCollaboratorIndex--;
|
|
205
|
-
if (nextActiveCollaboratorIndex < 0) {
|
|
206
|
-
nextActiveCollaboratorIndex = collaboratorsLen - 1;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
setActiveCollaboratorIndex(nextActiveCollaboratorIndex);
|
|
210
|
-
}, [searchedCollaborators, activeCollaboratorIndex]);
|
|
211
|
-
const onSelectCollaborator = useCallback(collaborator => {
|
|
212
|
-
const selection = window.getSelection();
|
|
213
|
-
const callBack = () => {
|
|
214
|
-
hideCommentPopover();
|
|
215
|
-
addParticipants(collaborator.username);
|
|
216
|
-
};
|
|
217
|
-
const newRange = commentUtilities.onSelectParticipant({
|
|
218
|
-
selection,
|
|
219
|
-
range,
|
|
220
|
-
participant: collaborator,
|
|
221
|
-
callBack,
|
|
222
|
-
commentRef: commentRef
|
|
223
|
-
});
|
|
224
|
-
setRange(newRange);
|
|
225
|
-
}, [range, commentUtilities, hideCommentPopover, addParticipants]);
|
|
226
|
-
const handleSelectCollaborator = useCallback(event => {
|
|
227
|
-
if (searchedCollaborators.length === 0) return;
|
|
228
|
-
onSelectCollaborator(searchedCollaborators[activeCollaboratorIndex]);
|
|
229
|
-
}, [searchedCollaborators, activeCollaboratorIndex, onSelectCollaborator]);
|
|
230
|
-
const onKeyDown = useCallback(event => {
|
|
231
|
-
if (Hotkey.isModZ(event)) {
|
|
232
|
-
event.preventDefault();
|
|
233
|
-
event.stopPropagation();
|
|
234
|
-
hideCommentPopover();
|
|
235
|
-
event.nativeEvent.stopImmediatePropagation();
|
|
236
|
-
commentRef.current.innerHTML = '';
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
if (Hotkey.isShiftEnter(event)) return;
|
|
240
|
-
if (Hotkey.isModP(event)) {
|
|
241
|
-
commentRef.current.blur();
|
|
242
|
-
}
|
|
243
|
-
if (event.keyCode === KeyCodes.Enter) {
|
|
244
|
-
event.preventDefault();
|
|
245
|
-
if (searchedCollaborators.length > 0) return;
|
|
246
|
-
onSubmit();
|
|
247
|
-
}
|
|
248
|
-
if (event.keyCode === KeyCodes.Esc) {
|
|
249
|
-
event.preventDefault();
|
|
250
|
-
onCancel();
|
|
251
|
-
}
|
|
252
|
-
if (event.keyCode === KeyCodes.UpArrow || event.keyCode === KeyCodes.DownArrow) {
|
|
253
|
-
event.stopPropagation();
|
|
254
|
-
if (searchedCollaborators.length > 0) {
|
|
255
|
-
event.preventDefault();
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}, [onCancel, onSubmit, searchedCollaborators, commentRef, hideCommentPopover]);
|
|
259
|
-
const onKeyUp = useCallback(event => {
|
|
260
|
-
const selection = window.getSelection();
|
|
261
|
-
setRange(selection.getRangeAt(0));
|
|
262
|
-
if (event.keyCode === KeyCodes.DownArrow) {
|
|
263
|
-
handleSelectingCollaborator(event, DOWN);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (event.keyCode === KeyCodes.UpArrow) {
|
|
267
|
-
handleSelectingCollaborator(event, UP);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (event.keyCode === KeyCodes.Enter) {
|
|
271
|
-
handleSelectCollaborator();
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
handleInvolvedKeyUp(event);
|
|
275
|
-
}, [handleSelectingCollaborator, handleInvolvedKeyUp, handleSelectCollaborator]);
|
|
276
|
-
const onMouseUp = useCallback(() => {
|
|
277
|
-
const selection = window.getSelection();
|
|
278
|
-
setRange(selection.getRangeAt(0));
|
|
279
|
-
}, []);
|
|
280
|
-
const onPaste = useCallback(event => {
|
|
281
|
-
commentUtilities.onPaste(event);
|
|
282
|
-
}, [commentUtilities]);
|
|
283
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
284
|
-
ref: commentRef,
|
|
285
|
-
contentEditable: "true",
|
|
286
|
-
className: "comment-editor",
|
|
287
|
-
placeholder: t(placeholder),
|
|
288
|
-
onKeyDown: onKeyDown,
|
|
289
|
-
onKeyUp: onKeyUp,
|
|
290
|
-
onMouseUp: onMouseUp,
|
|
291
|
-
onPaste: onPaste
|
|
292
|
-
}), searchedCollaborators.length > 0 && /*#__PURE__*/React.createElement(ModalPortal, null, /*#__PURE__*/React.createElement("div", {
|
|
293
|
-
className: "sdoc-comment-caret-list",
|
|
294
|
-
ref: collaboratorsPopoverRef
|
|
295
|
-
}, searchedCollaborators.map((participant, index) => {
|
|
296
|
-
return /*#__PURE__*/React.createElement(CommentParticipantItem, {
|
|
297
|
-
key: participant.username,
|
|
298
|
-
participantIndex: index,
|
|
299
|
-
activeParticipantIndex: activeCollaboratorIndex,
|
|
300
|
-
participant: participant,
|
|
301
|
-
setScrollTop: setScrollTop,
|
|
302
|
-
onSelectParticipant: onSelectCollaborator
|
|
303
|
-
});
|
|
304
|
-
}))));
|
|
305
|
-
});
|
|
306
|
-
export default CommentInput;
|