@seafile/sdoc-editor 2.0.68 → 2.0.70

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 (29) hide show
  1. package/dist/basic-sdk/comment/comment-decorate.js +17 -17
  2. package/dist/basic-sdk/comment/components/comment-context-menu/index.css +16 -0
  3. package/dist/basic-sdk/comment/components/comment-context-menu/index.js +38 -0
  4. package/dist/basic-sdk/comment/components/comment-context-menu/menu-item.js +57 -0
  5. package/dist/basic-sdk/comment/components/comment-editor.js +5 -2
  6. package/dist/basic-sdk/comment/components/comment-item-collapse-wrapper.js +17 -1
  7. package/dist/basic-sdk/comment/components/comment-item-wrapper.js +85 -30
  8. package/dist/basic-sdk/comment/components/comment-list.css +34 -0
  9. package/dist/basic-sdk/comment/components/comment-list.js +96 -20
  10. package/dist/basic-sdk/comment/components/editor-comment.js +117 -57
  11. package/dist/basic-sdk/comment/components/elements-comment-count/index.js +15 -3
  12. package/dist/basic-sdk/comment/components/global-comment/index.css +25 -0
  13. package/dist/basic-sdk/comment/helper.js +111 -2
  14. package/dist/basic-sdk/comment/hooks/notification-hooks/use-notification-mount.js +2 -1
  15. package/dist/basic-sdk/comment/reducer/comment-reducer.js +2 -3
  16. package/dist/basic-sdk/constants/index.js +2 -1
  17. package/dist/basic-sdk/editor/sdoc-editor.js +4 -4
  18. package/dist/basic-sdk/extension/plugins/ai/ai-icon/index.js +2 -2
  19. package/dist/basic-sdk/extension/plugins/ai/ai-module/helpers.js +4 -0
  20. package/dist/basic-sdk/extension/plugins/text-style/menu/index.js +2 -1
  21. package/dist/basic-sdk/extension/plugins/text-style/render-elem.js +28 -4
  22. package/dist/basic-sdk/hooks/use-selection-position.js +51 -30
  23. package/dist/basic-sdk/node-id/helpers.js +26 -2
  24. package/dist/basic-sdk/node-id/index.js +37 -2
  25. package/dist/components/doc-operations/revision-operations/index.js +1 -1
  26. package/dist/hooks/use-document.js +3 -1
  27. package/package.json +1 -1
  28. package/public/locales/en/sdoc-editor.json +2 -1
  29. package/public/locales/zh_CN/sdoc-editor.json +2 -1
@@ -8,10 +8,12 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.default = void 0;
9
9
  var _react = _interopRequireWildcard(require("react"));
10
10
  var _slate = require("@seafile/slate");
11
+ var _classnames = _interopRequireDefault(require("classnames"));
11
12
  var _constants = require("../../constants");
12
- var _constants2 = require("../../extension/constants");
13
+ var _helpers = require("../../extension/plugins/ai/ai-module/helpers");
13
14
  var _useSelectionElement = require("../../hooks/use-selection-element");
14
15
  var _useSelectionUpdate = _interopRequireDefault(require("../../hooks/use-selection-update"));
16
+ var _eventBus = _interopRequireDefault(require("../../utils/event-bus"));
15
17
  var _helper = require("../helper");
16
18
  var _useCommentContext = require("../hooks/comment-hooks/use-comment-context");
17
19
  var _index = require("../utils/index");
@@ -28,39 +30,84 @@ const EditorComment = _ref => {
28
30
  const currentSelectionElement = (0, _useSelectionElement.useSelectionElement)({
29
31
  editor
30
32
  }); // The slate node of the current cursor line
31
- const [activeElement, setActiveElement] = (0, _react.useState)(null); // The slate node currently activated by clicking
33
+ const [activeElementIds, setActiveElementIds] = (0, _react.useState)([]); // The elements currently activated by clicking
32
34
  const [isShowComments, setIsShowComments] = (0, _react.useState)(false);
33
35
  const [commentDetail, setCommentDetail] = (0, _react.useState)({});
34
- const onAddCommentToggle = (0, _react.useCallback)(event => {
35
- event.stopPropagation();
36
- let activeElement = currentSelectionElement;
37
- // Add comments to image elements in the image block
38
- if (currentSelectionElement.type === _constants2.ELEMENT_TYPE.IMAGE_BLOCK) {
39
- activeElement = currentSelectionElement.children.find(item => (item === null || item === void 0 ? void 0 : item.type) === _constants2.ELEMENT_TYPE.IMAGE);
40
- }
41
- setActiveElement(activeElement);
42
- setIsShowComments(true);
36
+ const [isContextComment, setIsContextComment] = (0, _react.useState)(false);
37
+ const [isClickedContextComment, setIsClickedContextComment] = (0, _react.useState)(false);
38
+ const commentedDomRef = (0, _react.useRef)(null);
39
+ const hiddenComment = (0, _react.useCallback)(() => {
43
40
  setCommentDetail({});
44
- }, [currentSelectionElement]);
45
- const onSelectElement = (0, _react.useCallback)(elementId => {
46
- const activeElement = (0, _index.getCommentElementById)(elementId, editor);
47
- setActiveElement(activeElement);
48
- const unresolvedComments = element_comments_map[elementId].filter(item => !item.resolved);
49
- setCommentDetail(unresolvedComments[0]);
41
+ setIsShowComments(false);
42
+ setIsContextComment(false);
43
+ (0, _helpers.removeMarks)(editor);
44
+ setIsClickedContextComment(false);
45
+ }, [editor]);
46
+ const onSelectElement = (0, _react.useCallback)(function (elementId) {
47
+ let isClickInContext = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
48
+ if (!isClickInContext) {
49
+ hiddenComment();
50
+ const activeElementIds = [elementId];
51
+ setActiveElementIds(activeElementIds);
52
+ const unresolvedComments = element_comments_map[elementId].filter(item => !item.resolved);
53
+ setCommentDetail({
54
+ ...unresolvedComments
55
+ });
56
+ setIsClickedContextComment(false);
57
+ }
50
58
  setIsShowComments(true);
51
- }, [editor, element_comments_map]);
59
+ if (isClickInContext) {
60
+ const clickedComments = [];
61
+ for (const comments of Object.values(editor.element_comments_map)) {
62
+ for (const comment of comments) {
63
+ if (elementId.includes(comment.detail.text_comment_id)) {
64
+ clickedComments.push(comment);
65
+ }
66
+ }
67
+ }
68
+ setCommentDetail({
69
+ ...clickedComments
70
+ });
71
+ setIsClickedContextComment(true);
72
+ }
73
+ }, [editor, element_comments_map, hiddenComment]);
74
+ (0, _react.useEffect)(() => {
75
+ const handleHoverContextComment = event => {
76
+ var _parentDom$className;
77
+ const parentDom = event.target.parentElement;
78
+ if (parentDom !== null && parentDom !== void 0 && (_parentDom$className = parentDom.className) !== null && _parentDom$className !== void 0 && _parentDom$className.includes('sdoc_comment_')) {
79
+ const isHover = event.type === 'mouseover';
80
+ const matchedAttributes = parentDom.className.split(' ').filter(cls => cls.startsWith('sdoc_comment_'));
81
+ matchedAttributes.forEach(className => {
82
+ const el = document.querySelectorAll(`.${className}`);
83
+ el.forEach(el => {
84
+ el.style.textDecoration = isHover ? 'underline #eb8205' : '';
85
+ el.style.textDecorationThickness = isHover ? '2px' : '';
86
+ });
87
+ });
88
+ }
89
+ };
90
+ document.addEventListener('mouseover', handleHoverContextComment);
91
+ document.addEventListener('mouseout', handleHoverContextComment);
92
+ return () => {
93
+ document.removeEventListener('mouseover', handleHoverContextComment);
94
+ document.removeEventListener('mouseout', handleHoverContextComment);
95
+ };
96
+ }, []);
52
97
  const onSetCommentDetail = (0, _react.useCallback)(comment => {
53
98
  setCommentDetail(comment);
54
99
  }, []);
55
- const hiddenComment = (0, _react.useCallback)(() => {
56
- setCommentDetail({});
57
- setIsShowComments(false);
58
- }, []);
59
100
 
60
101
  // Comments are updated to modify the current comment
61
102
  (0, _react.useEffect)(() => {
62
- if (activeElement) {
63
- const unresolvedComments = element_comments_map[activeElement.id].filter(item => !item.resolved);
103
+ if (isContextComment && activeElementIds) {
104
+ const unresolvedComments = element_comments_map[activeElementIds[0].element.id].filter(item => !item.resolved);
105
+ if (unresolvedComments.length === 0) {
106
+ setIsShowComments(false);
107
+ }
108
+ }
109
+ if (activeElementIds && !Array.isArray(activeElementIds)) {
110
+ const unresolvedComments = element_comments_map[activeElementIds.id].filter(item => !item.resolved);
64
111
  if (unresolvedComments.length === 0) {
65
112
  setIsShowComments(false);
66
113
  }
@@ -72,53 +119,66 @@ const EditorComment = _ref => {
72
119
  if (isShowComments) {
73
120
  hiddenComment();
74
121
  }
75
- // eslint-disable-next-line react-hooks/exhaustive-deps
76
- }, [currentSelectionElement]);
77
- const cursor = (0, _helper.useCursorPosition)();
78
- const style = (0, _react.useMemo)(() => {
79
- var _Node$string;
80
- if (currentSelectionElement && ((_Node$string = _slate.Node.string(currentSelectionElement)) === null || _Node$string === void 0 ? void 0 : _Node$string.length) === 0 && !currentSelectionElement.children.find(n => n.type === _constants2.ELEMENT_TYPE.IMAGE)) {
81
- return {
82
- top: '-99999px'
83
- };
122
+ if (isContextComment) {
123
+ (0, _helpers.removeMarks)(editor);
84
124
  }
85
- const comments = element_comments_map[currentSelectionElement === null || currentSelectionElement === void 0 ? void 0 : currentSelectionElement.id];
86
- const unresolvedComments = comments && comments.filter(item => !item.resolved);
87
- const hasComments = unresolvedComments && unresolvedComments.length > 0;
88
- if (hasComments) return {
89
- top: '-99999px'
125
+ // eslint-disable-next-line react-hooks/exhaustive-deps
126
+ }, [currentSelectionElement, editor.selection]);
127
+ (0, _react.useEffect)(() => {
128
+ const handleAddContextComment = () => {
129
+ // Add temporary marks for selection
130
+ _slate.Editor.addMark(editor, 'comment', true);
131
+ setTimeout(() => {
132
+ const activeElementIds = (0, _helper.getSelectedElemIds)(editor);
133
+ const lastSelectedDom = (0, _helper.getDomById)(activeElementIds[activeElementIds.length - 1]);
134
+ commentedDomRef.current = lastSelectedDom;
135
+ setActiveElementIds(activeElementIds);
136
+ setIsShowComments(true);
137
+ setCommentDetail({});
138
+ setIsContextComment(true);
139
+ }, 0);
90
140
  };
91
- if (cursor.y === 0 || isShowComments) return {
92
- top: '-99999px'
141
+ const handleClickCommentedText = event => {
142
+ const parentDom = event.target.parentElement;
143
+ if (parentDom.className.split(/\s+/).some(cls => cls.startsWith('sdoc_comment'))) {
144
+ commentedDomRef.current = parentDom;
145
+ const matchedAttributes = parentDom.className.split(' ').filter(cls => cls.startsWith('sdoc_comment_'));
146
+ const clickedCommmentIdArray = matchedAttributes.map(item => item.replace('sdoc_comment_', ''));
147
+ onSelectElement(clickedCommmentIdArray, true);
148
+ }
93
149
  };
94
- return {
95
- top: cursor.y,
96
- zIndex: _constants.Z_INDEX.COMMENT_ADD
150
+ const eventBus = _eventBus.default.getInstance();
151
+ const unsubscribeAddContextComment = eventBus.subscribe(_constants.INTERNAL_EVENT.ADD_CONTEXT_COMMENT, handleAddContextComment);
152
+ document.addEventListener('click', handleClickCommentedText);
153
+ return () => {
154
+ unsubscribeAddContextComment();
155
+ document.removeEventListener('click', handleClickCommentedText);
97
156
  };
98
- }, [currentSelectionElement, element_comments_map, cursor.y, isShowComments]);
157
+ }, [editor, onSelectElement]);
99
158
  return /*#__PURE__*/_react.default.createElement("div", {
100
159
  className: "sdoc-comment-container"
101
160
  }, /*#__PURE__*/_react.default.createElement("div", {
102
161
  className: "comment-container-main"
103
162
  }), /*#__PURE__*/_react.default.createElement("div", {
104
- className: "comment-container-right"
105
- }, /*#__PURE__*/_react.default.createElement("div", {
106
- className: "comment-add-wrapper",
107
- style: style
108
- }, /*#__PURE__*/_react.default.createElement("span", {
109
- className: "add-comment-icon",
110
- onClick: onAddCommentToggle
111
- }, /*#__PURE__*/_react.default.createElement("i", {
112
- className: "sdocfont sdoc-add-comment mr-1"
113
- }))), /*#__PURE__*/_react.default.createElement(_elementsCommentCount.default, {
163
+ className: (0, _classnames.default)('comment-container-right', {
164
+ 'isContextComment': isContextComment
165
+ })
166
+ }, /*#__PURE__*/_react.default.createElement(_elementsCommentCount.default, {
114
167
  elementCommentsMap: element_comments_map,
115
- activeElement: activeElement,
168
+ activeElementIds: activeElementIds,
116
169
  editor: editor,
117
170
  onSelectElement: onSelectElement
118
171
  }), isShowComments && /*#__PURE__*/_react.default.createElement(_commentList.default, {
119
- activeElement: activeElement,
172
+ activeElementIds: activeElementIds,
120
173
  commentDetail: commentDetail,
121
- onSetCommentDetail: onSetCommentDetail
174
+ onSetCommentDetail: onSetCommentDetail,
175
+ isContextComment: isContextComment,
176
+ isClickedContextComment: isClickedContextComment,
177
+ setIsClickedContextComment: setIsClickedContextComment,
178
+ onSelectElement: onSelectElement,
179
+ commentedDom: commentedDomRef.current,
180
+ closeComment: hiddenComment,
181
+ editor: editor
122
182
  })));
123
183
  };
124
184
  var _default = exports.default = EditorComment;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.default = void 0;
8
8
  var _react = _interopRequireDefault(require("react"));
9
+ var _utils = require("../../utils");
9
10
  var _elementCommentCount = _interopRequireDefault(require("./element-comment-count"));
10
11
  require("./index.css");
11
12
  const ElementsCommentCount = _ref => {
@@ -18,14 +19,25 @@ const ElementsCommentCount = _ref => {
18
19
  if (!elementCommentsMap) return null;
19
20
  return /*#__PURE__*/_react.default.createElement("div", {
20
21
  className: "elements-comments-count"
21
- }, Object.keys(elementCommentsMap).map(elementId => {
22
- const comments = elementCommentsMap[elementId];
22
+ }, Object.keys(elementCommentsMap).map(originElementId => {
23
+ var _comments$0$detail$el, _comments$0$detail;
24
+ const comments = elementCommentsMap[originElementId];
23
25
  if (!Array.isArray(comments) || comments.length === 0) return null;
26
+ let elementId = originElementId;
27
+ const elementIdList = (_comments$0$detail$el = (_comments$0$detail = comments[0].detail) === null || _comments$0$detail === void 0 ? void 0 : _comments$0$detail.element_id_list) !== null && _comments$0$detail$el !== void 0 ? _comments$0$detail$el : [];
28
+ if (elementIdList.length > 1) {
29
+ const existedId = elementIdList.find(id => (0, _utils.getCommentElementById)(id, editor));
30
+ if (existedId) {
31
+ elementId = existedId;
32
+ } else {
33
+ return null;
34
+ }
35
+ }
24
36
  const unresolvedComment = comments.filter(item => !item.resolved);
25
37
  const unresolvedCommentCount = unresolvedComment.length;
26
38
  if (unresolvedCommentCount === 0) return null;
27
39
  return /*#__PURE__*/_react.default.createElement(_elementCommentCount.default, {
28
- key: elementId,
40
+ key: `${originElementId}-${elementId}`,
29
41
  elementId: elementId,
30
42
  isElementSelected: selectionElement && selectionElement.id === elementId,
31
43
  commentsCount: unresolvedCommentCount,
@@ -278,6 +278,31 @@
278
278
  overflow: hidden;
279
279
  }
280
280
 
281
+ .sdoc-comment-drawer .sdoc-comment-list-container .detail-context-comment {
282
+ align-items: start !important;
283
+ }
284
+
285
+ .sdoc-comment-drawer .sdoc-comment-list-container .detail-context-comment .sdoc-comment-quote {
286
+ padding-top: 1px;
287
+ }
288
+
289
+ .sdoc-comment-drawer .sdoc-comment-list-container .context-comment-item-selected-text-container {
290
+ padding: 16px 16px 0 16px;
291
+ color: #666;
292
+ display: flex;
293
+ }
294
+
295
+ .sdoc-comment-drawer .sdoc-comment-list-container .context-comment-item-selected-text-container .sdoc-comment-quote {
296
+ font-size: 12px;
297
+ padding-top: 1px;
298
+ }
299
+
300
+ .sdoc-comment-drawer .sdoc-comment-list-container .context-comment-item-selected-text-container .context-comment-items {
301
+ display: flex;
302
+ flex-direction: column;
303
+ font-size: 14px;
304
+ }
305
+
281
306
  .comments-panel-body__header .sdoc-comment-filter-dropdown.sdoc-dropdown-menu {
282
307
  border: 1px solid #dee3eb;
283
308
  }
@@ -4,9 +4,11 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.useCursorPosition = exports.getSelectionRange = exports.getElementCommentCountTop = exports.getCursorPosition = exports.getAvatarUrl = void 0;
7
+ exports.useCursorPosition = exports.updateElementsAttrs = exports.updateCommentedElementsAttrs = exports.getSelectionRange = exports.getSelectedElemIds = exports.getPrimaryElementId = exports.getElementCommentCountTop = exports.getDomById = exports.getCursorPosition = exports.getCommentedTextsByElementId = exports.getAvatarUrl = void 0;
8
+ var _slate = require("@seafile/slate");
8
9
  var _slateReact = require("@seafile/slate-react");
9
10
  var _context = _interopRequireDefault(require("../../context"));
11
+ var _core = require("../extension/core");
10
12
  var _useScrollContext = require("../hooks/use-scroll-context");
11
13
  const getSelectionRange = () => {
12
14
  if (window.getSelection) {
@@ -72,4 +74,111 @@ const getElementCommentCountTop = (editor, element, scrollTop) => {
72
74
  });
73
75
  return minY - 93 + scrollTop; // 100: header height(56) + toolbar height(37)
74
76
  };
75
- exports.getElementCommentCountTop = getElementCommentCountTop;
77
+ exports.getElementCommentCountTop = getElementCommentCountTop;
78
+ const getSelectedElemIds = editor => {
79
+ const {
80
+ selection
81
+ } = editor;
82
+ if (!selection) return;
83
+ const selectedElemId = [];
84
+ const nodeEntries = Array.from(_slate.Editor.nodes(editor, {
85
+ match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n),
86
+ mode: 'lowest'
87
+ }));
88
+ for (const [node] of nodeEntries) {
89
+ selectedElemId.push(node.id);
90
+ }
91
+ return selectedElemId;
92
+ };
93
+ exports.getSelectedElemIds = getSelectedElemIds;
94
+ const getCommentedTextsByElementId = (elementId, textCommentId) => {
95
+ const container = document.querySelector(`[data-id='${elementId}']`);
96
+ if (!container) return [];
97
+ let targetDoms = container.querySelectorAll(`.sdoc_comment_${textCommentId}`);
98
+ if (targetDoms.length === 0) {
99
+ targetDoms = container.querySelectorAll(`.removed_sdoc_comment_${textCommentId}`);
100
+ }
101
+ const texts = [];
102
+ targetDoms.forEach(dom => {
103
+ var _dom$textContent;
104
+ texts.push(((_dom$textContent = dom.textContent) === null || _dom$textContent === void 0 ? void 0 : _dom$textContent.trim()) || '');
105
+ });
106
+ return texts;
107
+ };
108
+ exports.getCommentedTextsByElementId = getCommentedTextsByElementId;
109
+ const getDomById = elementId => {
110
+ const container = document.querySelector(`[data-id='${elementId}']`);
111
+ if (!container) return [];
112
+ const lastCommentedDomWithMarks = container.querySelector('.comment');
113
+ return lastCommentedDomWithMarks;
114
+ };
115
+ exports.getDomById = getDomById;
116
+ const updateElementsAttrs = (activeElementIds, editor, text_comment_id) => {
117
+ if (Array.isArray(activeElementIds)) {
118
+ for (const elemId of activeElementIds) {
119
+ const dom = document.querySelectorAll(`[data-id="${elemId}"]`)[0];
120
+ if (!dom) continue;
121
+ const domNode = _slateReact.ReactEditor.toSlateNode(editor, dom);
122
+ const nodePath = (0, _core.findPath)(editor, domNode);
123
+ domNode.children.forEach((textNode, index) => {
124
+ if (textNode.comment) {
125
+ const textNodePath = [...nodePath, index];
126
+ _slate.Transforms.setNodes(editor, {
127
+ [`sdoc_comment_${text_comment_id}`]: true
128
+ }, {
129
+ at: textNodePath,
130
+ match: _slate.Text.isText,
131
+ split: true
132
+ });
133
+ }
134
+ });
135
+ }
136
+ }
137
+ };
138
+ exports.updateElementsAttrs = updateElementsAttrs;
139
+ const updateCommentedElementsAttrs = function (activeElementIds, editor, text_comment_id) {
140
+ let resolved = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
141
+ let isDeleteComment = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
142
+ if (Array.isArray(activeElementIds)) {
143
+ _slate.Editor.withoutNormalizing(editor, () => {
144
+ for (const elemId of activeElementIds) {
145
+ // const { element } = elem;
146
+ const dom = document.querySelector(`[data-id="${elemId}"]`);
147
+ if (!dom) continue;
148
+ const domNode = _slateReact.ReactEditor.toSlateNode(editor, dom);
149
+ const domNodePath = (0, _core.findPath)(editor, domNode);
150
+ domNode.children.forEach((child, index) => {
151
+ const childPath = [...domNodePath, index];
152
+ if (_slate.Text.isText(child) && `sdoc_comment_${text_comment_id}` in child) {
153
+ _slate.Transforms.unsetNodes(editor, [`sdoc_comment_${text_comment_id}`], {
154
+ at: childPath,
155
+ match: _slate.Text.isText
156
+ });
157
+ !isDeleteComment && _slate.Transforms.setNodes(editor, {
158
+ [`sdoc_comment_${text_comment_id}`]: resolved
159
+ }, {
160
+ at: childPath,
161
+ match: _slate.Text.isText
162
+ });
163
+ }
164
+ });
165
+ }
166
+ });
167
+ }
168
+ };
169
+ exports.updateCommentedElementsAttrs = updateCommentedElementsAttrs;
170
+ const getPrimaryElementId = detail => {
171
+ if (!detail) return null;
172
+ let elementId;
173
+ const {
174
+ element_id,
175
+ element_id_list
176
+ } = detail;
177
+ if (element_id_list.length > 0) {
178
+ elementId = element_id_list[0];
179
+ } else {
180
+ elementId = element_id;
181
+ }
182
+ return elementId;
183
+ };
184
+ exports.getPrimaryElementId = getPrimaryElementId;
@@ -69,6 +69,7 @@ const useNotificationsMount = dispatch => {
69
69
  return () => {
70
70
  unsubscribeNewNotification();
71
71
  };
72
- }, [request]);
72
+ // eslint-disable-next-line react-hooks/exhaustive-deps
73
+ }, []);
73
74
  };
74
75
  exports.useNotificationsMount = useNotificationsMount;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.sortCommentList = exports.initElementCommentsMap = exports.initCommentsInfo = exports.initCommentList = exports.formatCommentsData = exports.commentReducer = void 0;
8
8
  var _deepCopy = _interopRequireDefault(require("deep-copy"));
9
+ var _helper = require("../helper");
9
10
  const formatCommentsData = comments => {
10
11
  const formatComments = [];
11
12
  const dupComments = (0, _deepCopy.default)(comments);
@@ -29,9 +30,7 @@ const initElementCommentsMap = comments => {
29
30
  const formatComments = formatCommentsData(comments);
30
31
  for (let i = 0; i < formatComments.length; i++) {
31
32
  const item = formatComments[i];
32
- const {
33
- element_id
34
- } = item.detail;
33
+ const element_id = (0, _helper.getPrimaryElementId)(item.detail);
35
34
  if (!elementCommentsMap[element_id]) {
36
35
  elementCommentsMap[element_id] = [];
37
36
  }
@@ -49,7 +49,8 @@ const INTERNAL_EVENT = exports.INTERNAL_EVENT = {
49
49
  RESIZE_ARTICLE: 'resize_article',
50
50
  ON_VIDEO_FILES_UPLOADED: 'on_video_files_uploaded',
51
51
  RELOAD_COMMENT: 'reload_comment',
52
- ASK_AI: 'ask_ai'
52
+ ASK_AI: 'ask_ai',
53
+ ADD_CONTEXT_COMMENT: 'add_context_comment'
53
54
  };
54
55
  const REVISION_DIFF_KEY = exports.REVISION_DIFF_KEY = 'diff';
55
56
  const REVISION_DIFF_VALUE = exports.REVISION_DIFF_VALUE = '1';
@@ -122,12 +122,12 @@ const SdocEditor = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
122
122
  const eventBus = _basicSdk.EventBus.getInstance();
123
123
  eventBus.subscribe(_constants.EXTERNAL_EVENT.REFRESH_DOCUMENT, onRefreshDocument);
124
124
 
125
- // Remove aiMarks on special conditions like unexpected exit or refresh page using AI
126
- const hasAIMark = !_slate.Editor.nodes(validEditor, {
125
+ // Remove Marks on special conditions like unexpected exit or refresh page using AI or context comment
126
+ const hasSpecialMark = !_slate.Editor.nodes(validEditor, {
127
127
  at: [],
128
- match: n => _slate.Text.isText(n) && n.sdoc_ai === true
128
+ match: n => _slate.Text.isText(n) && (n.sdoc_ai === true || n.comment === true)
129
129
  }).next().done;
130
- if (hasAIMark) {
130
+ if (hasSpecialMark) {
131
131
  (0, _helpers.removeMarks)(validEditor);
132
132
  }
133
133
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -17,9 +17,9 @@ function AIIcon(_ref) {
17
17
  const {
18
18
  t
19
19
  } = (0, _reactI18next.useTranslation)('sdoc-editor');
20
- const clazznames = (0, _classnames.default)('sdoc-ask-ai-icon', className);
20
+ const classnames = (0, _classnames.default)('sdoc-ask-ai-icon', className);
21
21
  return /*#__PURE__*/_react.default.createElement("img", {
22
- className: clazznames,
22
+ className: classnames,
23
23
  src: _sdocAskAi.default,
24
24
  alt: t('Ask_AI')
25
25
  });
@@ -13,6 +13,10 @@ const removeMarks = editor => {
13
13
  at: [],
14
14
  match: n => _slate.Text.isText(n) && n.sdoc_ai === true
15
15
  });
16
+ _slate.Transforms.unsetNodes(editor, 'comment', {
17
+ at: [],
18
+ match: n => _slate.Text.isText(n) && n.comment === true
19
+ });
16
20
  if (selection) {
17
21
  _slate.Transforms.select(editor, selection);
18
22
  } else {
@@ -11,6 +11,7 @@ var _reactI18next = require("react-i18next");
11
11
  var _slate = require("@seafile/slate");
12
12
  var _constants = require("../../../../../basic-sdk/constants");
13
13
  var _context = _interopRequireDefault(require("../../../../../context"));
14
+ var _commentContextMenu = _interopRequireDefault(require("../../../../comment/components/comment-context-menu"));
14
15
  var _useColorContext = require("../../../../hooks/use-color-context");
15
16
  var _eventBus = _interopRequireDefault(require("../../../../utils/event-bus"));
16
17
  var _mouseEvent = require("../../../../utils/mouse-event");
@@ -155,7 +156,7 @@ const TextStyleMenuList = _ref => {
155
156
  tipMessage: t('Reduce_font_size')
156
157
  }, /*#__PURE__*/_react.default.createElement("i", {
157
158
  className: "sdocfont sdoc-reduce-font-size"
158
- }))), idPrefix && enableSeafileAI && /*#__PURE__*/_react.default.createElement(_aiMenu.AIContextMenu, {
159
+ }))), idPrefix && /*#__PURE__*/_react.default.createElement(_commentContextMenu.default, null), idPrefix && enableSeafileAI && /*#__PURE__*/_react.default.createElement(_aiMenu.AIContextMenu, {
159
160
  isRichEditor: isRichEditor
160
161
  }));
161
162
  };
@@ -15,7 +15,7 @@ const renderText = props => {
15
15
  children,
16
16
  leaf
17
17
  } = props;
18
- const {
18
+ let {
19
19
  text,
20
20
  ...rest
21
21
  } = leaf;
@@ -35,10 +35,34 @@ const renderText = props => {
35
35
  style['minWidth'] = '2px';
36
36
  }
37
37
 
38
- // Add temporary marks for selection in AI
39
- if (leaf.sdoc_ai && leaf.text.trim()) {
38
+ // Add temporary marks for selection in AI or context comment
39
+ if ((leaf.sdoc_ai || leaf.comment) && leaf.text.trim()) {
40
40
  style['padding'] = '3.23px 0';
41
- style['background'] = '#a9c9ed';
41
+ style['backgroundColor'] = '#a9c9ed';
42
+ }
43
+
44
+ // Background color overlap for multi comments
45
+ if (Object.keys(leaf).some(key => key.startsWith('sdoc_comment_'))) {
46
+ const commentEntries = Object.entries(leaf).filter(_ref => {
47
+ let [key] = _ref;
48
+ return key.startsWith('sdoc_comment_');
49
+ });
50
+ for (const [key, value] of commentEntries) {
51
+ if (value === false && key.startsWith('sdoc_comment_')) {
52
+ const newKey = `removed_${key}`;
53
+ rest[newKey] = true;
54
+ delete rest[key];
55
+ }
56
+ }
57
+ const commentRest = Object.keys(rest).filter(item => item.startsWith('sdoc_comment_'));
58
+
59
+ // Multi comment or only one comment
60
+ if (commentRest.length > 1) {
61
+ style['backgroundColor'] = 'rgba(129, 237, 247)';
62
+ }
63
+ if (commentRest.length === 1) {
64
+ style['backgroundColor'] = 'rgba(129, 237, 247, 0.5)';
65
+ }
42
66
  }
43
67
  if (leaf.computed_background_color) {
44
68
  style['backgroundColor'] = leaf.computed_background_color;
@@ -3,45 +3,66 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.useSelectionPosition = exports.useCommentListPosition = void 0;
7
- var _slateReact = require("@seafile/slate-react");
6
+ exports.useCommentListPosition = void 0;
8
7
  var _useScrollContext = require("./use-scroll-context");
9
- const useSelectionPosition = node => {
10
- const editor = (0, _slateReact.useSlateStatic)();
11
- if (!editor.selection) return {
12
- x: 0,
13
- y: 0
14
- };
15
- if (!node) return {
16
- x: 0,
17
- y: 0
18
- };
19
- try {
20
- const domNode = _slateReact.ReactEditor.toDOMNode(editor, node);
21
- const rect = domNode.getBoundingClientRect();
22
- return rect;
23
- } catch (error) {
24
- // A new node has not yet been indexed, it cannot be retrieved, and the new node has no comment information, just do not display it
25
- return {
26
- x: 0,
27
- y: 0
28
- };
29
- }
30
- };
31
- exports.useSelectionPosition = useSelectionPosition;
32
- const useCommentListPosition = selectionElement => {
33
- const selectionPosition = useSelectionPosition(selectionElement);
8
+ const useCommentListPosition = (selectedElementIds, isContextComment, isClickedContextComment, commentedDom, commentDetail, closeComment) => {
9
+ var _document$querySelect;
34
10
  const headerHeight = 100;
35
11
  const scrollRef = (0, _useScrollContext.useScrollContext)();
36
12
  const {
37
13
  scrollTop = 0
38
14
  } = scrollRef.current || {};
39
- if (selectionPosition.y !== 0) {
15
+ if (isContextComment || isClickedContextComment) {
16
+ let rect;
17
+ if (isContextComment) {
18
+ rect = commentedDom.getBoundingClientRect();
19
+ }
20
+
21
+ // rect is the last commented context dom
22
+ if (isClickedContextComment) {
23
+ const lastComment = Object.values(commentDetail).map(item => {
24
+ var _item$detail;
25
+ return (_item$detail = item.detail) === null || _item$detail === void 0 ? void 0 : _item$detail.text_comment_id;
26
+ }).flatMap(id => {
27
+ const elements = Array.from(document.querySelectorAll(`.sdoc_comment_${id}`));
28
+ return elements.map(el => ({
29
+ id,
30
+ el
31
+ }));
32
+ }).reduce((last, current) => {
33
+ if (!last) return current;
34
+ const position = last.el.compareDocumentPosition(current.el);
35
+ return position & Node.DOCUMENT_POSITION_FOLLOWING ? current : last;
36
+ }, null);
37
+ if (lastComment) {
38
+ const result = `.sdoc_comment_${lastComment.id}`;
39
+ const elements = Array.from(document.querySelectorAll(result));
40
+ if (elements.length === 0) return;
41
+ const lastTextElement = elements[elements.length - 1];
42
+ const parentElement = lastTextElement.closest('[data-slate-node="element"]');
43
+ const firstInSameParent = elements.find(el => parentElement.contains(el));
44
+ rect = firstInSameParent === null || firstInSameParent === void 0 ? void 0 : firstInSameParent.getBoundingClientRect();
45
+ } else {
46
+ closeComment();
47
+ }
48
+ }
49
+ const editorArticleRight = document.getElementById('sdoc-editor-print-wrapper').getBoundingClientRect().right;
50
+ const topPara = rect.bottom - headerHeight + 10 + scrollTop;
51
+ const rightPara = editorArticleRight - rect.left - 300; // 300 is comment container's width
52
+ return {
53
+ right: rightPara,
54
+ y: topPara
55
+ };
56
+ }
57
+ const selectionPosition = (_document$querySelect = document.querySelectorAll(`[data-id="${selectedElementIds[0]}"]`)[0]) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.getBoundingClientRect();
58
+ // Boundary check
59
+ if (!selectionPosition) closeComment();
60
+ if (selectionPosition && selectionPosition.y !== 0) {
40
61
  selectionPosition.y = selectionPosition.y - headerHeight + scrollTop;
41
62
  }
42
63
  return {
43
- x: selectionPosition.x,
44
- y: selectionPosition.y
64
+ x: selectionPosition === null || selectionPosition === void 0 ? void 0 : selectionPosition.x,
65
+ y: selectionPosition === null || selectionPosition === void 0 ? void 0 : selectionPosition.y
45
66
  };
46
67
  };
47
68
  exports.useCommentListPosition = useCommentListPosition;