@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
@@ -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
- const TextStyleMenuList = _ref => {
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
- } = _ref;
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
- const list = getTextStyleList(TEXT_STYLE);
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
- return /*#__PURE__*/React.createElement(MenuItem, Object.assign({
106
- key: index
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 = changes[currentDiffIndex];
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
- }, [changes, currentDiffIndex]);
35
+ }, [topLevelChanges, currentDiffIndex]);
26
36
  const lastChange = useCallback(() => {
27
37
  if (currentDiffIndex === 0) {
28
- jumpToElement(changes.length - 1);
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
- }, [changes, currentDiffIndex]);
44
+ }, [topLevelChanges, currentDiffIndex]);
35
45
  const nextChange = useCallback(() => {
36
- if (currentDiffIndex === changes.length - 1) {
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
- }, [changes, currentDiffIndex]);
44
- if (!Array.isArray(changes) || changes.length === 0) {
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 = changes.length;
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.url,
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -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;