@seafile/sdoc-editor 0.4.26 → 0.4.28

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.
@@ -44,4 +44,10 @@ export const CALLOUT_COLOR_MAP = {
44
44
  background_color: '#fde8ff'
45
45
  }
46
46
  };
47
+ export const CALLOUT_ICON_MAP = {
48
+ 'trumpet': '📢',
49
+ 'bulb': '💡',
50
+ 'prohibited': '🚫',
51
+ 'warning': '⚠️'
52
+ };
47
53
  export const CALLOUT_ALLOWED_INSIDE_TYPES = [CALL_OUT, ORDERED_LIST, UNORDERED_LIST, PARAGRAPH, TITLE, SUBTITLE, BLOCKQUOTE, ...HEADERS, ...LIST_ITEM_CORRELATION_TYPE, CHECK_LIST_ITEM, IMAGE, LINK, SDOC_LINK];
@@ -80,6 +80,22 @@ export const changeFillBackgroundColor = (editor, background_color) => {
80
80
  });
81
81
  Transforms.select(editor, Editor.start(editor, editor.selection));
82
82
  };
83
+ export const setCalloutIcon = (editor, icon_name) => {
84
+ Transforms.setNodes(editor, {
85
+ callout_icon: icon_name
86
+ }, {
87
+ match: n => n.type === CALL_OUT
88
+ });
89
+ Transforms.select(editor, Editor.start(editor, editor.selection));
90
+ };
91
+ export const deleteCalloutIcon = editor => {
92
+ Transforms.setNodes(editor, {
93
+ callout_icon: ''
94
+ }, {
95
+ match: n => n.type === CALL_OUT
96
+ });
97
+ Transforms.select(editor, Editor.start(editor, editor.selection));
98
+ };
83
99
 
84
100
  // Check is cursor in callout
85
101
  export const getCalloutEntry = function (editor) {
@@ -2,7 +2,7 @@ import isHotkey from 'is-hotkey';
2
2
  import { Editor, Transforms } from '@seafile/slate';
3
3
  import { PARAGRAPH, INSERT_POSITION, CODE_BLOCK } from '../../constants';
4
4
  import { isSelectionAtBlockStart } from '../../core';
5
- import { getCalloutEntry, isCalloutContentEmpty, unwrapCallout } from './helper';
5
+ import { deleteCalloutIcon, getCalloutEntry, isCalloutContentEmpty, unwrapCallout } from './helper';
6
6
  import { insertElement } from '../../toolbar/side-toolbar/helpers';
7
7
  import EventBus from '../../../utils/event-bus';
8
8
  import { INTERNAL_EVENT } from '../../../constants';
@@ -23,6 +23,11 @@ const withCallout = editor => {
23
23
  newEditor.deleteBackward = unit => {
24
24
  const calloutEntry = getCalloutEntry(editor);
25
25
  if (calloutEntry) {
26
+ const node = calloutEntry[0];
27
+ if (isSelectionAtBlockStart(editor) && !!node.callout_icon) {
28
+ deleteCalloutIcon(editor);
29
+ return;
30
+ }
26
31
  if (isSelectionAtBlockStart(editor) && isCalloutContentEmpty(calloutEntry)) {
27
32
  unwrapCallout(editor);
28
33
  return;
@@ -1,34 +1,38 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { Transforms } from '@seafile/slate';
3
- import { CALLOUT_COLOR_MAP } from '../constant';
4
- import { findPath } from '../../../core';
5
- import { changeFillBackgroundColor } from '../helper';
6
- import { ElementPopover } from '../../../commons';
7
- import './index.css';
3
+ import { CALLOUT_COLOR_MAP } from '../../constant';
4
+ import { findPath } from '../../../../core';
5
+ import { changeFillBackgroundColor } from '../../helper';
6
+ import './style.css';
8
7
  const ColorSelector = _ref => {
9
8
  let {
10
9
  editor,
11
10
  element,
12
- popoverPosition
11
+ onCloseSelector
13
12
  } = _ref;
14
13
  const onColorClick = useCallback(event => {
14
+ event.stopPropagation();
15
+ let target = event.target;
16
+ while (!target.dataset || !target.dataset.backgroundColor) {
17
+ target = target.parentNode;
18
+ }
15
19
  const {
16
20
  backgroundColor
17
- } = event.target.dataset;
21
+ } = target.dataset;
18
22
  const currentPath = findPath(editor, element);
19
23
  Transforms.select(editor, currentPath);
20
24
  changeFillBackgroundColor(editor, backgroundColor);
21
- }, [editor, element]);
25
+ onCloseSelector();
26
+ }, [editor, element, onCloseSelector]);
22
27
  const isShowCheckedIcon = useCallback(currentBackgroundColor => {
23
28
  const {
24
29
  background_color
25
30
  } = element.style || {};
26
31
  return background_color && background_color === currentBackgroundColor;
27
32
  }, [element.style]);
28
- return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
33
+ return /*#__PURE__*/React.createElement("div", {
29
34
  className: "sdoc-callout-color-selector-container",
30
- contentEditable: false,
31
- style: popoverPosition
35
+ contentEditable: false
32
36
  }, /*#__PURE__*/React.createElement("ul", {
33
37
  className: "sdoc-color-selector-list"
34
38
  }, Object.values(CALLOUT_COLOR_MAP).map((_ref2, index) => {
@@ -49,6 +53,6 @@ const ColorSelector = _ref => {
49
53
  }, isShowCheckedIcon(background_color) && /*#__PURE__*/React.createElement("i", {
50
54
  className: "sdoc-callout-color-checked-icon sdocfont sdoc-check-mark"
51
55
  }));
52
- }))));
56
+ })));
53
57
  };
54
58
  export default ColorSelector;
@@ -0,0 +1,39 @@
1
+ .sdoc-callout-color-selector-container {
2
+ position: absolute;
3
+ padding: 10px;
4
+ left: 0;
5
+ top: 36.5px;
6
+ background-color: #fff;
7
+ border: 1px solid #eee;
8
+ border-radius: 3px;
9
+ z-index: 100;
10
+ }
11
+
12
+ .sdoc-callout-color-selector-container .sdoc-color-selector-list {
13
+ display: flex;
14
+ margin: 0;
15
+ padding: 0;
16
+ }
17
+
18
+ .sdoc-callout-color-selector-container .sdoc-color-selector-list .sdoc-callout-color-item {
19
+ position: relative;
20
+ margin-right: 10px;
21
+ width: 20px;
22
+ height: 20px;
23
+ list-style: none;
24
+ border-width: 1px;
25
+ border-style: solid;
26
+ border-radius: 3px;
27
+ cursor: pointer;
28
+ }
29
+
30
+ .sdoc-callout-color-selector-container .sdoc-color-selector-list .sdoc-callout-color-item:last-child {
31
+ margin-right: 0px;
32
+ }
33
+
34
+ .sdoc-callout-color-selector-container .sdoc-color-selector-list .sdoc-callout-color-item .sdoc-callout-color-checked-icon {
35
+ position: absolute;
36
+ top: 1px;
37
+ left: 3px;
38
+ font-size: 12px;
39
+ }
@@ -0,0 +1,69 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import { ElementPopover } from '../../../../commons';
4
+ import ColorSelector from '../callout-color-selector';
5
+ import IconSelector from '../callout-icon';
6
+ import './style.css';
7
+ export default function CalloutHoverMenu(_ref) {
8
+ let {
9
+ editor,
10
+ element,
11
+ popoverPosition
12
+ } = _ref;
13
+ const [isShowColorSelector, setIsShowColorSelector] = useState(false);
14
+ const [isShowIcon, setIsShowIcon] = useState(false);
15
+ const onColorSelectorToggle = useCallback(event => {
16
+ event.stopPropagation();
17
+ if (!isShowColorSelector) {
18
+ setIsShowIcon(false);
19
+ }
20
+ setIsShowColorSelector(!isShowColorSelector);
21
+ }, [isShowColorSelector, setIsShowColorSelector]);
22
+ const onIconToggle = useCallback(event => {
23
+ event.stopPropagation();
24
+ if (!isShowIcon) {
25
+ setIsShowColorSelector(false);
26
+ }
27
+ setIsShowIcon(!isShowIcon);
28
+ }, [isShowIcon, setIsShowIcon]);
29
+ const onCloseSelector = useCallback(() => {
30
+ setIsShowColorSelector(false);
31
+ setIsShowIcon(false);
32
+ }, []);
33
+ const firstItemClass = classNames({
34
+ 'callout-menu-item': true,
35
+ 'color-active': isShowColorSelector
36
+ });
37
+ const secondItemClass = classNames({
38
+ 'callout-menu-item': true,
39
+ 'icon-active': isShowIcon
40
+ });
41
+ return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
42
+ className: "sdoc-callout-hover-menu",
43
+ style: popoverPosition
44
+ }, /*#__PURE__*/React.createElement("div", {
45
+ className: firstItemClass,
46
+ onClick: onColorSelectorToggle
47
+ }, /*#__PURE__*/React.createElement("span", {
48
+ className: "sdocfont sdoc-callout-color mr-1"
49
+ }), /*#__PURE__*/React.createElement("span", {
50
+ className: "sdocfont sdoc-drop-down"
51
+ })), /*#__PURE__*/React.createElement("div", {
52
+ className: "callout-menu-divider"
53
+ }), /*#__PURE__*/React.createElement("div", {
54
+ className: secondItemClass,
55
+ onClick: onIconToggle
56
+ }, /*#__PURE__*/React.createElement("span", {
57
+ className: "sdocfont sdoc-callout-icon mr-1"
58
+ }), /*#__PURE__*/React.createElement("span", {
59
+ className: "sdocfont sdoc-drop-down"
60
+ })), isShowColorSelector && /*#__PURE__*/React.createElement(ColorSelector, {
61
+ editor: editor,
62
+ element: element,
63
+ onCloseSelector: onCloseSelector
64
+ }), isShowIcon && /*#__PURE__*/React.createElement(IconSelector, {
65
+ editor: editor,
66
+ element: element,
67
+ onCloseSelector: onCloseSelector
68
+ })));
69
+ }
@@ -0,0 +1,43 @@
1
+ .sdoc-callout-hover-menu {
2
+ position: absolute;
3
+ padding: 7px;
4
+ height: 36px;
5
+ background-color: #fff;
6
+ border: 1px solid #eee;
7
+ border-radius: 3px;
8
+ z-index: 100;
9
+ display: flex;
10
+ }
11
+
12
+ .sdoc-callout-hover-menu .callout-menu-item {
13
+ padding: 0 4px;
14
+ display: flex;
15
+ justify-content: center;
16
+ align-items: center;
17
+ color: #aaa;
18
+ cursor: pointer;
19
+ }
20
+
21
+ .sdoc-callout-hover-menu .callout-menu-item:hover {
22
+ color: #444;
23
+ }
24
+
25
+ .sdoc-callout-hover-menu .callout-menu-item .sdocfont {
26
+ font-size: 12px;
27
+ color: #444;
28
+ }
29
+
30
+ .sdoc-callout-hover-menu .callout-menu-item .sdoc-drop-down {
31
+ color: #999;
32
+ }
33
+
34
+ .sdoc-callout-hover-menu .callout-menu-item.color-active,
35
+ .sdoc-callout-hover-menu .callout-menu-item.icon-active {
36
+ background-color: #f2f2f2;
37
+ border-radius: 2px;
38
+ }
39
+
40
+ .sdoc-callout-hover-menu .callout-menu-divider {
41
+ border-left: 1px solid #ccc;
42
+ margin: 4px 8px;
43
+ }
@@ -0,0 +1,38 @@
1
+ import React, { useCallback } from 'react';
2
+ import { Transforms } from '@seafile/slate';
3
+ import { findPath } from '../../../../core';
4
+ import { setCalloutIcon } from '../../helper';
5
+ import { CALLOUT_ICON_MAP } from '../../constant';
6
+ import './style.css';
7
+ const IconSelector = _ref => {
8
+ let {
9
+ editor,
10
+ element,
11
+ onCloseSelector
12
+ } = _ref;
13
+ const onImageClick = useCallback(event => {
14
+ event.stopPropagation();
15
+ const {
16
+ image
17
+ } = event.target.dataset;
18
+ if (!image) return;
19
+ const currentPath = findPath(editor, element);
20
+ Transforms.select(editor, currentPath);
21
+ setCalloutIcon(editor, image);
22
+ onCloseSelector();
23
+ }, [editor, element, onCloseSelector]);
24
+ return /*#__PURE__*/React.createElement("div", {
25
+ className: "sdoc-callout-icon-selector-container",
26
+ onClick: onImageClick
27
+ }, Object.keys(CALLOUT_ICON_MAP).map(key => {
28
+ const content = CALLOUT_ICON_MAP[key];
29
+ return /*#__PURE__*/React.createElement("div", {
30
+ key: key,
31
+ className: "icon-item"
32
+ }, /*#__PURE__*/React.createElement("span", {
33
+ className: 'sdoc-emoji ' + key,
34
+ "data-image": key
35
+ }, content));
36
+ }));
37
+ };
38
+ export default IconSelector;
@@ -0,0 +1,33 @@
1
+ .sdoc-callout-icon-selector-container {
2
+ position: absolute;
3
+ padding: 4px;
4
+ left: 40px;
5
+ top: 36.5px;
6
+ background-color: #fff;
7
+ border: 1px solid #eee;
8
+ border-radius: 3px;
9
+ display: flex;
10
+ min-width: 100px;
11
+ z-index: 100;
12
+ }
13
+
14
+ .sdoc-callout-icon-selector-container .icon-item {
15
+ width: 32px;
16
+ height: 32px;
17
+ margin: 0 4px;
18
+ cursor: pointer;
19
+ display: flex;
20
+ justify-content: center;
21
+ align-items: center;
22
+ }
23
+
24
+ .sdoc-callout-icon-selector-container .icon-item:hover {
25
+ background-color: #f2f2f2;
26
+ }
27
+
28
+ .sdoc-callout-icon-selector-container .icon-item img {
29
+ display: inline-block;
30
+ width: 24px;
31
+ height: 24px;
32
+ cursor: pointer;
33
+ }
@@ -8,50 +8,41 @@
8
8
  border-width: 1px;
9
9
  border-style: solid;
10
10
  border-radius: 5px;
11
+ display: flex;
12
+ }
13
+
14
+ .sdoc-callout-container .callout-content {
15
+ flex: 1;
11
16
  }
12
17
 
13
18
  .sdoc-callout-container .sdoc-callout-placeholder {
14
19
  position: absolute;
15
20
  top: 15px;
16
- left: 10px;
21
+ left: 26px;
17
22
  color: #b8b6b6;
18
23
  pointer-events: none;
19
24
  }
20
25
 
21
- .sdoc-callout-color-selector-container {
22
- position: absolute;
23
- padding: 10px;
24
- background-color: #fff;
25
- border: 1px solid #eee;
26
- border-radius: 3px;
27
- z-index: 100;
26
+ .sdoc-emoji {
27
+ font-family: "Apple Color Emoji", "Segoe UI Emoji", NotoColorEmoji, "Noto Color Emoji", "Segoe UI Symbol", "Android Emoji", EmojiSymbols;
28
+ white-space: nowrap;
29
+ height: 24px;
30
+ width: 24px;
31
+ line-height: 24px;
32
+ font-size: 21px;
28
33
  }
29
34
 
30
- .sdoc-callout-color-selector-container .sdoc-color-selector-list {
31
- display: flex;
32
- margin: 0;
33
- padding: 0;
35
+ .sdoc-emoji.wraning {
36
+ color: block;
34
37
  }
35
38
 
36
- .sdoc-callout-color-selector-container .sdoc-color-selector-list .sdoc-callout-color-item {
37
- position: relative;
38
- margin-right: 10px;
39
- width: 16px;
40
- height: 16px;
41
- list-style: none;
42
- border-width: 1px;
43
- border-style: solid;
44
- border-radius: 3px;
45
- cursor: pointer;
39
+ .sdoc-callout-container .callout-icon {
40
+ width: 18px;
41
+ margin-right: 8px;
42
+ margin-top: 2px;
46
43
  }
47
44
 
48
- .sdoc-callout-color-selector-container .sdoc-color-selector-list .sdoc-callout-color-item:last-child {
49
- margin-right: 0px;
45
+ .sdoc-callout-container .callout-icon .sdoc-emoji {
46
+ font-size: 16px;
50
47
  }
51
48
 
52
- .sdoc-callout-color-selector-container .sdoc-color-selector-list .sdoc-callout-color-item .sdoc-callout-color-checked-icon {
53
- position: absolute;
54
- top: -2px;
55
- font-size: 12px;
56
- left: 1px;
57
- }
@@ -3,11 +3,11 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
3
  import { Node } from '@seafile/slate';
4
4
  import { useReadOnly, useSelected } from '@seafile/slate-react';
5
5
  import { useTranslation } from 'react-i18next';
6
- import ColorSelector from './color-selector';
7
- import { CALLOUT_COLOR_MAP } from '../constant';
6
+ import { CALLOUT_COLOR_MAP, CALLOUT_ICON_MAP } from '../constant';
8
7
  import { INTERNAL_EVENT } from '../../../../constants';
9
8
  import EventBus from '../../../../utils/event-bus';
10
9
  import { useScrollContext } from '../../../../hooks/use-scroll-context';
10
+ import CalloutHoverMenu from './callout-hover-menu';
11
11
  import './index.css';
12
12
  const renderCallout = (_ref, editor) => {
13
13
  let {
@@ -41,6 +41,13 @@ const renderCallout = (_ref, editor) => {
41
41
  borderColor
42
42
  };
43
43
  }, [element.style, isSelected]);
44
+ const calloutIcon = useMemo(() => {
45
+ const {
46
+ callout_icon = ''
47
+ } = element;
48
+ if (!callout_icon) return null;
49
+ return CALLOUT_ICON_MAP[callout_icon];
50
+ }, [element]);
44
51
  const isShowPlaceholder = useCallback(() => {
45
52
  if (isSelected) return false;
46
53
  // If element contains more than one element or element is not paragraph, show placeholder
@@ -60,7 +67,7 @@ const renderCallout = (_ref, editor) => {
60
67
  top,
61
68
  left
62
69
  } = calloutRef.current.getBoundingClientRect();
63
- const menuTop = top - 42; // top = top distance - menu height
70
+ const menuTop = top - 36; // top = top distance - menu height
64
71
  const newMenuPosition = {
65
72
  top: menuTop,
66
73
  left: left // left = code-block left distance
@@ -84,6 +91,11 @@ const renderCallout = (_ref, editor) => {
84
91
  observerRefValue.removeEventListener('scroll', handleScroll);
85
92
  };
86
93
  }, [handleScroll, readOnly, scrollRef]);
94
+ useEffect(() => {
95
+ if (!isSelected) {
96
+ setIsShowColorSelector(false);
97
+ }
98
+ }, [isSelected]);
87
99
  const handleDisplayColorSelector = useCallback(() => {
88
100
  if (readOnly) return;
89
101
  const {
@@ -103,17 +115,22 @@ const renderCallout = (_ref, editor) => {
103
115
  }, [handleDisplayColorSelector]);
104
116
  return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
105
117
  "data-id": element.id,
106
- className: "sdoc-callout-white-wrapper",
107
- onMouseLeave: handleCloseColorSelector
118
+ className: "sdoc-callout-white-wrapper"
108
119
  }), /*#__PURE__*/React.createElement("div", {
109
120
  onClick: handleClick,
110
121
  ref: calloutRef,
111
122
  className: "".concat(attributes.className, " sdoc-callout-container"),
112
123
  style: containerStyle
124
+ }, /*#__PURE__*/React.createElement("div", {
125
+ className: "callout-icon"
126
+ }, element.callout_icon && /*#__PURE__*/React.createElement("span", {
127
+ className: 'sdoc-emoji ' + element.callout_icon
128
+ }, calloutIcon)), /*#__PURE__*/React.createElement("div", {
129
+ className: "callout-content"
113
130
  }, children, isShowPlaceholder() && /*#__PURE__*/React.createElement("div", {
114
131
  contentEditable: false,
115
132
  className: "sdoc-callout-placeholder"
116
- }, t('Please_enter...')), isShowColorSelector && /*#__PURE__*/React.createElement(ColorSelector, {
133
+ }, t('Please_enter...'))), isShowColorSelector && /*#__PURE__*/React.createElement(CalloutHoverMenu, {
117
134
  editor: editor,
118
135
  element: element,
119
136
  popoverPosition: popoverPosition
@@ -234,7 +234,15 @@ export const scrollIntoView = (articleContainerTop, highlightX, highlightY, code
234
234
  };
235
235
  const getNowrapCodeBlockInfos = editor => {
236
236
  const codeBlockEntries = Editor.nodes(editor, {
237
- match: n => n.type === CODE_BLOCK && n.style['white_space'] === 'nowrap',
237
+ match: n => {
238
+ var _n$style;
239
+ const isCodeBlock = Element.isElement(n) && n.type === CODE_BLOCK;
240
+ if (!isCodeBlock) return false;
241
+ // To compatible with old version,which has no style property with white_space, by default, it is 'nowrap'
242
+ const nodeWhiteSpace = (n === null || n === void 0 ? void 0 : (_n$style = n.style) === null || _n$style === void 0 ? void 0 : _n$style['white_space']) || 'nowrap';
243
+ if (nodeWhiteSpace === 'nowrap') return true;
244
+ return false;
245
+ },
238
246
  at: []
239
247
  }) || [];
240
248
 
@@ -8,6 +8,7 @@ import { DateUtils } from '../../utils';
8
8
  import DraftDropdown from '../draft-dropdown';
9
9
  import RevisionAvatar from '../../assets/images/revision-avatar.png';
10
10
  import FileTagQuickView from '../doc-operations/tag-operation/file-tag-quick-view';
11
+ import freezedImg from '../../basic-sdk/assets/images/sdoc-freezed.png';
11
12
  import './index.css';
12
13
  const DocInfo = _ref => {
13
14
  let {
@@ -55,9 +56,10 @@ const DocInfo = _ref => {
55
56
  onClick: onInternalLinkClick
56
57
  })), isFreezed && /*#__PURE__*/React.createElement("span", {
57
58
  className: "doc-icon"
58
- }, /*#__PURE__*/React.createElement("span", {
59
- className: "sdocfont sdoc-freezed",
60
- title: t('Freezed')
59
+ }, /*#__PURE__*/React.createElement("img", {
60
+ src: freezedImg,
61
+ alt: t('Freezed'),
62
+ width: "16px"
61
63
  })), /*#__PURE__*/React.createElement(FileTagQuickView, null), /*#__PURE__*/React.createElement(TipMessage, {
62
64
  isEditMode: isEditMode
63
65
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.4.26",
3
+ "version": "0.4.28",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",