@seafile/sdoc-editor 0.3.13 → 0.3.15

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.
@@ -13,7 +13,7 @@ export const INTERNAL_EVENT = {
13
13
  UPDATE_TAG_VIEW: 'update_tag_view',
14
14
  COMMENT_LIST_CLICK: 'comment_list_click',
15
15
  UNSEEN_NOTIFICATIONS_COUNT: 'unseen_notifications_count',
16
- DISPLAY_CALLOUT_COLOR_PICKER: 'display_callout_color_picker'
16
+ CLOSE_CALLOUT_COLOR_PICKER: 'close_callout_color_picker'
17
17
  };
18
18
  export const REVISION_DIFF_KEY = 'diff';
19
19
  export const REVISION_DIFF_VALUE = '1';
@@ -1,4 +1,4 @@
1
- import { Editor, Path, Transforms } from '@seafile/slate';
1
+ import { Editor, Node, Path, Transforms } from '@seafile/slate';
2
2
  import { CALL_OUT } from '../../constants/element-type';
3
3
  import { findPath, focusEditor, generateEmptyElement, getSelectedElems, isRangeAcrossBlocks } from '../../core';
4
4
  import { CALLOUT_ALLOWED_INSIDE_TYPES, CALLOUT_COLOR_MAP } from './constant';
@@ -100,10 +100,10 @@ export const isFocusOnElement = (editor, element) => {
100
100
  const isSelectOnElement = Path.isAncestor(elementPath, editor === null || editor === void 0 ? void 0 : (_editor$selection = editor.selection) === null || _editor$selection === void 0 ? void 0 : (_editor$selection$anc = _editor$selection.anchor) === null || _editor$selection$anc === void 0 ? void 0 : _editor$selection$anc.path);
101
101
  return isSelectOnElement;
102
102
  };
103
- export const isCalloutContentEmpty = (editor, calloutEntry) => {
104
- const [, calloutPath] = calloutEntry;
105
- const contentString = Editor.string(editor, calloutPath);
106
- return contentString.length === 0;
103
+ export const isCalloutContentEmpty = calloutEntry => {
104
+ const [calloutNode] = calloutEntry;
105
+ const contentString = Node.string(calloutNode);
106
+ return calloutNode.children.length === 1 && contentString.length === 0;
107
107
  };
108
108
 
109
109
  // Insert a new element at new line in callout after current path
@@ -1,5 +1,5 @@
1
1
  import isHotkey from 'is-hotkey';
2
- import { PARAGRAPH, INSERT_POSITION } from '../../constants';
2
+ import { PARAGRAPH, INSERT_POSITION, CODE_BLOCK } from '../../constants';
3
3
  import { isSelectionAtBlockStart } from '../../core';
4
4
  import { getCalloutEntry, isCalloutContentEmpty, unwrapCallout } from './helper';
5
5
  import { insertElement } from '../../toolbar/side-toolbar/helpers';
@@ -15,39 +15,50 @@ const withCallout = editor => {
15
15
  const {
16
16
  insertFragment,
17
17
  deleteBackward,
18
- onHotKeyDown
18
+ onHotKeyDown,
19
+ insertData
19
20
  } = editor;
20
21
  const newEditor = editor;
21
22
  newEditor.deleteBackward = unit => {
22
23
  const calloutEntry = getCalloutEntry(editor);
23
24
  if (calloutEntry) {
24
- if (isSelectionAtBlockStart(editor) && isCalloutContentEmpty(editor, calloutEntry)) return;
25
+ if (isSelectionAtBlockStart(editor) && isCalloutContentEmpty(calloutEntry)) {
26
+ unwrapCallout(editor);
27
+ return;
28
+ }
25
29
  }
26
30
  return deleteBackward(unit);
27
31
  };
32
+ newEditor.insertData = data => {
33
+ if (getCalloutEntry(newEditor)) {
34
+ if (data.types.includes('text/code-block')) {
35
+ const eventBus = EventBus.getInstance();
36
+ eventBus.dispatch(INTERNAL_EVENT.DISPLAY_CALLOUT_UNSUPPORT_ALERT, CODE_BLOCK);
37
+ return;
38
+ }
39
+ }
40
+ return insertData(data);
41
+ };
28
42
  newEditor.insertFragment = data => {
29
- var _data$find;
30
- const eventBus = EventBus.getInstance();
31
- const unsupportType = (_data$find = data.find(node => !CALLOUT_ALLOWED_INSIDE_TYPES.includes(node.type))) === null || _data$find === void 0 ? void 0 : _data$find.type;
32
- if (unsupportType) {
33
- eventBus.dispatch(INTERNAL_EVENT.DISPLAY_CALLOUT_UNSUPPORT_ALERT, unsupportType);
34
- return;
43
+ if (getCalloutEntry(editor)) {
44
+ var _data$find;
45
+ const eventBus = EventBus.getInstance();
46
+ const unsupportType = (_data$find = data.find(node => !CALLOUT_ALLOWED_INSIDE_TYPES.includes(node.type))) === null || _data$find === void 0 ? void 0 : _data$find.type;
47
+ if (unsupportType) {
48
+ eventBus.dispatch(INTERNAL_EVENT.DISPLAY_CALLOUT_UNSUPPORT_ALERT, unsupportType);
49
+ return;
50
+ }
35
51
  }
36
52
  return insertFragment(data);
37
53
  };
38
54
  newEditor.onHotKeyDown = event => {
39
- if (isHotkey('mod+enter', event)) {
40
- insertElement(newEditor, PARAGRAPH, INSERT_POSITION.AFTER);
41
- return true;
42
- }
43
- if (isHotkey('enter', event)) {
44
- const calloutEntry = getCalloutEntry(editor);
45
- if (calloutEntry) {
46
- if (isCalloutContentEmpty(editor, calloutEntry)) {
47
- unwrapCallout(editor);
48
- event.preventDefault();
49
- return true;
50
- }
55
+ if (getCalloutEntry(editor)) {
56
+ // Close color picker
57
+ const eventBus = EventBus.getInstance();
58
+ eventBus.dispatch(INTERNAL_EVENT.CLOSE_CALLOUT_COLOR_PICKER);
59
+ if (isHotkey('mod+enter', event)) {
60
+ insertElement(newEditor, PARAGRAPH, INSERT_POSITION.AFTER);
61
+ return true;
51
62
  }
52
63
  }
53
64
  return onHotKeyDown && onHotKeyDown(event);
@@ -3,11 +3,13 @@ import { Transforms } from '@seafile/slate';
3
3
  import { CALLOUT_COLOR_MAP } from '../constant';
4
4
  import { findPath } from '../../../core';
5
5
  import { changeFillBackgroundColor } from '../helper';
6
+ import { ElementPopover } from '../../../commons';
6
7
  import './index.css';
7
8
  const ColorSelector = (_ref, ref) => {
8
9
  let {
9
10
  editor,
10
- element
11
+ element,
12
+ popoverPosition
11
13
  } = _ref;
12
14
  const selectorRef = useRef(null);
13
15
  const handleClickColor = useCallback((editor, element, background_color) => {
@@ -24,10 +26,14 @@ const ColorSelector = (_ref, ref) => {
24
26
  } = element.style;
25
27
  return background_color && background_color === currentBackgroundColor;
26
28
  }, []);
27
- return /*#__PURE__*/React.createElement("div", {
28
- ref: selectorRef,
29
+ return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
29
30
  className: "sdoc-callout-color-selector-container",
30
- contentEditable: false
31
+ ref: selectorRef,
32
+ contentEditable: false,
33
+ style: {
34
+ top: popoverPosition.top,
35
+ left: popoverPosition.left
36
+ }
31
37
  }, /*#__PURE__*/React.createElement("ul", {
32
38
  className: "sdoc-color-selector-list"
33
39
  }, Object.values(CALLOUT_COLOR_MAP).map((_ref2, index) => {
@@ -46,6 +52,6 @@ const ColorSelector = (_ref, ref) => {
46
52
  }, isShowCheckedIcon(element, background_color) && /*#__PURE__*/React.createElement("i", {
47
53
  className: "sdoc-callout-color-checked-icon sdocfont sdoc-check-mark"
48
54
  }));
49
- })));
55
+ }))));
50
56
  };
51
57
  export default forwardRef(ColorSelector);
@@ -21,12 +21,10 @@
21
21
  .sdoc-callout-color-selector-container {
22
22
  position: absolute;
23
23
  padding: 10px;
24
- top: -39px;
25
- left: 5px;
26
24
  background-color: #fff;
27
- box-shadow: -2px 0px 8px 2px #d0cfcf;
28
25
  border: 1px solid #eee;
29
26
  border-radius: 3px;
27
+ z-index: 100;
30
28
  }
31
29
 
32
30
  .sdoc-callout-color-selector-container .sdoc-color-selector-list {
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable react-hooks/rules-of-hooks */
2
2
  import React, { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { useReadOnly } from '@seafile/slate-react';
3
4
  import { useTranslation } from 'react-i18next';
4
5
  import ColorSelector from './color-selector';
5
6
  import { CALLOUT_COLOR_MAP } from '../constant';
@@ -8,6 +9,7 @@ import { Editor } from '@seafile/slate';
8
9
  import { findPath } from '../../../core';
9
10
  import { INTERNAL_EVENT } from '../../../../constants';
10
11
  import EventBus from '../../../../utils/event-bus';
12
+ import { useScrollContext } from '../../../../hooks/use-scroll-context';
11
13
  import './index.css';
12
14
  const renderCallout = (_ref, editor) => {
13
15
  let {
@@ -16,8 +18,13 @@ const renderCallout = (_ref, editor) => {
16
18
  element
17
19
  } = _ref;
18
20
  const [isShowColorSelector, setIsShowColorSelector] = useState(false);
19
- const [isSelecting, setIsSelecting] = useState(false);
20
- const calloutRef = useRef(null);
21
+ const [popoverPosition, setPopoverPosition] = useState({
22
+ top: '',
23
+ left: ''
24
+ });
25
+ const readOnly = useReadOnly();
26
+ const scrollRef = useScrollContext();
27
+ const calloutRef = useRef();
21
28
  const colorSelectorRef = useRef({
22
29
  colorSelectorContainer: null
23
30
  });
@@ -28,25 +35,60 @@ const renderCallout = (_ref, editor) => {
28
35
  background_color
29
36
  } = element.style;
30
37
  const isFocusOnCallout = isFocusOnElement(editor, element);
31
- const handleMouseEnter = useCallback(id => {
32
- const isMoveInCurrentCallout = id === element.id;
33
- isMoveInCurrentCallout && !isSelecting && setIsShowColorSelector(true);
34
- }, [element.id, isSelecting]);
38
+ const handleCloseColorSelector = useCallback(() => {
39
+ setIsShowColorSelector(false);
40
+ }, []);
41
+ const handleScroll = useCallback(e => {
42
+ if (readOnly) return;
43
+ if (!isShowColorSelector) return;
44
+ if (e.currentTarget.scrollTop) {
45
+ const {
46
+ top,
47
+ left
48
+ } = calloutRef.current.getBoundingClientRect();
49
+ const menuTop = top - 42; // top = top distance - menu height
50
+ const newMenuPosition = {
51
+ top: menuTop,
52
+ left: left // left = code-block left distance
53
+ };
54
+
55
+ setPopoverPosition(newMenuPosition);
56
+ }
57
+ }, [isShowColorSelector, readOnly]);
35
58
  useEffect(() => {
36
59
  const eventBus = EventBus.getInstance();
37
- const unsubscribe = eventBus.subscribe(INTERNAL_EVENT.DISPLAY_CALLOUT_COLOR_PICKER, handleMouseEnter);
60
+ const unsubscribe = eventBus.subscribe(INTERNAL_EVENT.CLOSE_CALLOUT_COLOR_PICKER, handleCloseColorSelector);
38
61
  return unsubscribe;
39
- }, [handleMouseEnter]);
40
- const handleMouseLeave = useCallback(e => {
41
- setIsShowColorSelector(false);
42
- setIsSelecting(false);
43
- }, []);
62
+ }, [handleCloseColorSelector]);
63
+ useEffect(() => {
64
+ if (readOnly) return;
65
+ let observerRefValue = null;
66
+ if (scrollRef.current) {
67
+ scrollRef.current.addEventListener('scroll', handleScroll);
68
+ observerRefValue = scrollRef.current;
69
+ }
70
+ return () => {
71
+ observerRefValue.removeEventListener('scroll', handleScroll);
72
+ };
73
+ }, [handleScroll, readOnly, scrollRef]);
74
+ const handleDisplayColorSelector = useCallback(() => {
75
+ if (readOnly) return;
76
+ const {
77
+ top,
78
+ left
79
+ } = calloutRef.current.getBoundingClientRect();
80
+ const menuTop = top - 42; // top = top distance - menu height
81
+ const newMenuPosition = {
82
+ top: menuTop,
83
+ left: left // left = callout left distance
84
+ };
85
+
86
+ setPopoverPosition(newMenuPosition);
87
+ setIsShowColorSelector(true);
88
+ }, [readOnly]);
44
89
  const handleClick = useCallback(e => {
45
- var _colorSelectorRef$cur, _colorSelectorRef$cur2;
46
- setIsSelecting(true);
47
- const isClickInColorSelector = (_colorSelectorRef$cur = colorSelectorRef.current) === null || _colorSelectorRef$cur === void 0 ? void 0 : (_colorSelectorRef$cur2 = _colorSelectorRef$cur.colorSelectorContainer) === null || _colorSelectorRef$cur2 === void 0 ? void 0 : _colorSelectorRef$cur2.contains(e.target);
48
- !isClickInColorSelector && setIsShowColorSelector(false);
49
- }, []);
90
+ handleDisplayColorSelector();
91
+ }, [handleDisplayColorSelector]);
50
92
  const isShowPlaceholder = useCallback(() => {
51
93
  if (isFocusOnCallout) return false;
52
94
  // If element contains more than one element or element is not paragraph, show placeholder
@@ -56,27 +98,27 @@ const renderCallout = (_ref, editor) => {
56
98
  const elementContent = Editor.string(editor, elementPath);
57
99
  return !elementContent.length;
58
100
  }, [editor, element, isFocusOnCallout]);
59
- return /*#__PURE__*/React.createElement("div", {
60
- className: "sdoc-callout-white-wrapper"
61
- }, /*#__PURE__*/React.createElement("div", Object.assign({
101
+ return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
62
102
  "data-id": element.id,
63
103
  "data-root": "true",
64
- onMouseLeave: handleMouseLeave,
104
+ className: "sdoc-callout-white-wrapper",
105
+ onMouseLeave: handleCloseColorSelector
106
+ }), /*#__PURE__*/React.createElement("div", {
65
107
  onClick: handleClick,
66
108
  ref: calloutRef,
109
+ className: "".concat(attributes.className, " sdoc-callout-container"),
67
110
  style: {
68
111
  backgroundColor: background_color ? CALLOUT_COLOR_MAP[background_color].background_color : 'transparent',
69
112
  borderColor: isFocusOnCallout ? CALLOUT_COLOR_MAP[background_color].border_color : 'transparent'
70
113
  }
71
- }, attributes, {
72
- className: "".concat(attributes.className, " sdoc-callout-container")
73
- }), children, isShowPlaceholder() && /*#__PURE__*/React.createElement("div", {
114
+ }, children, isShowPlaceholder() && /*#__PURE__*/React.createElement("div", {
74
115
  contentEditable: false,
75
116
  className: "sdoc-callout-placeholder"
76
117
  }, t('Please_enter...')), isShowColorSelector && /*#__PURE__*/React.createElement(ColorSelector, {
77
118
  ref: colorSelectorRef,
78
119
  editor: editor,
79
- element: element
120
+ element: element,
121
+ popoverPosition: popoverPosition
80
122
  })));
81
123
  };
82
124
  export default renderCallout;
@@ -18,9 +18,6 @@ const TableSizePopover = _ref => {
18
18
  const [displaySize, setDisplaySize] = useState([5, 10]);
19
19
  const [selectedSize, setSelectedSize] = useState([1, 1]);
20
20
  const ref = useRef(null);
21
- const {
22
- t
23
- } = useTranslation();
24
21
  const onMouseEnter = useCallback(function (event) {
25
22
  let cellPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [1, 1];
26
23
  let newDisplaySize = displaySize.slice(0);
@@ -1,8 +1,6 @@
1
1
  import { Editor } from '@seafile/slate';
2
2
  import { findPath } from '../core';
3
- import { CALL_OUT, MOUSE_ENTER_EVENT_DISABLED_MAP } from '../constants';
4
- import { INTERNAL_EVENT } from '../../constants';
5
- import EventBus from '../../utils/event-bus';
3
+ import { MOUSE_ENTER_EVENT_DISABLED_MAP } from '../constants';
6
4
  import { onMouseEnter } from '../toolbar/side-toolbar/event';
7
5
  const isNeedAddMouseEnterEvent = (editor, element) => {
8
6
  const elementPath = findPath(editor, element);
@@ -20,18 +18,4 @@ const isNeedAddMouseEnterEvent = (editor, element) => {
20
18
  export const setMouseEnter = (editor, element, attributes) => {
21
19
  if (!isNeedAddMouseEnterEvent(editor, element)) return;
22
20
  attributes['onMouseEnter'] = e => onMouseEnter(e, element);
23
- };
24
-
25
- // Element extra event
26
- export const handleElementMouseEnterEvent = element => {
27
- const {
28
- id,
29
- type
30
- } = element;
31
- const eventBus = EventBus.getInstance();
32
- const elementEventMap = {
33
- [CALL_OUT]: eventBus.dispatch(INTERNAL_EVENT.DISPLAY_CALLOUT_COLOR_PICKER, id)
34
- };
35
- const event = elementEventMap[type];
36
- event && event();
37
21
  };
@@ -1,13 +1,9 @@
1
1
  import EventBus from '../../../utils/event-bus';
2
2
  import { INTERNAL_EVENT } from '../../../constants';
3
- import { CALL_OUT } from '../../constants';
4
- import { handleElementMouseEnterEvent } from '../../render/helper';
5
3
  export const onMouseEnter = (event, element) => {
6
4
  event.stopPropagation();
7
5
  const eventBus = EventBus.getInstance();
8
6
  eventBus.dispatch(INTERNAL_EVENT.ON_MOUSE_ENTER_BLOCK, event);
9
- element.type === CALL_OUT && eventBus.dispatch(INTERNAL_EVENT.DISPLAY_CALLOUT_COLOR_PICKER);
10
- handleElementMouseEnterEvent(element);
11
7
  };
12
8
  export const onDragOver = event => {
13
9
  event.stopPropagation();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -414,5 +414,8 @@
414
414
  "New": "Nový",
415
415
  "Table_template": "Table template",
416
416
  "Jump_to_original_doc": "Jump to the original document",
417
- "Freezed": "Freezed"
417
+ "Freezed": "Freezed",
418
+ "Callout": "Callout",
419
+ "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
+ "Please_enter...": "Please enter..."
418
421
  }
@@ -414,5 +414,8 @@
414
414
  "New": "Erstellen",
415
415
  "Table_template": "Table template",
416
416
  "Jump_to_original_doc": "Jump to the original document",
417
- "Freezed": "Freezed"
417
+ "Freezed": "Freezed",
418
+ "Callout": "Callout",
419
+ "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
+ "Please_enter...": "Please enter..."
418
421
  }
@@ -414,5 +414,8 @@
414
414
  "New": "Nuevo",
415
415
  "Table_template": "Table template",
416
416
  "Jump_to_original_doc": "Jump to the original document",
417
- "Freezed": "Freezed"
417
+ "Freezed": "Freezed",
418
+ "Callout": "Callout",
419
+ "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
+ "Please_enter...": "Please enter..."
418
421
  }
@@ -414,5 +414,8 @@
414
414
  "New": "Créer",
415
415
  "Table_template": "Table template",
416
416
  "Jump_to_original_doc": "Jump to the original document",
417
- "Freezed": "Freezed"
417
+ "Freezed": "Freezed",
418
+ "Callout": "Callout",
419
+ "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
+ "Please_enter...": "Please enter..."
418
421
  }
@@ -414,5 +414,8 @@
414
414
  "New": "Nuovo",
415
415
  "Table_template": "Table template",
416
416
  "Jump_to_original_doc": "Jump to the original document",
417
- "Freezed": "Freezed"
417
+ "Freezed": "Freezed",
418
+ "Callout": "Callout",
419
+ "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
+ "Please_enter...": "Please enter..."
418
421
  }
@@ -281,7 +281,7 @@
281
281
  "Revision": "Пересмотр",
282
282
  "Error": "Ошибка",
283
283
  "Start_revise": "Начать пересмотр",
284
- "Revise": "Revise",
284
+ "Revise": "Пересмотр",
285
285
  "Failed_to_execute_operation_on_server": "Не удалось выполнить операцию на сервере, текущая операция отменена.",
286
286
  "Start_revise_tip": "Создайте временный документ и внесите в него изменения, объедините его обратно после просмотра изменений",
287
287
  "Load_doc_content_error": "Ошибка загрузки содержимого документа",
@@ -414,5 +414,8 @@
414
414
  "New": "Новый",
415
415
  "Table_template": "Шаблон таблицы",
416
416
  "Jump_to_original_doc": "Перейти к исходному документу",
417
- "Freezed": "Freezed"
417
+ "Freezed": "Заморожено",
418
+ "Callout": "Callout",
419
+ "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
+ "Please_enter...": "Please enter..."
418
421
  }