@seafile/sdoc-editor 0.3.29 → 0.4.1

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 (24) hide show
  1. package/dist/api/seafile-api.js +7 -5
  2. package/dist/basic-sdk/constants/index.js +2 -2
  3. package/dist/basic-sdk/extension/plugins/blockquote/plugin.js +47 -55
  4. package/dist/basic-sdk/extension/plugins/image/helpers.js +5 -0
  5. package/dist/basic-sdk/extension/plugins/image/hover-menu/index.js +13 -6
  6. package/dist/basic-sdk/extension/plugins/image/render-elem.js +8 -7
  7. package/dist/basic-sdk/extension/plugins/sdoc-link/helpers.js +9 -3
  8. package/dist/basic-sdk/extension/plugins/sdoc-link/plugin.js +2 -2
  9. package/dist/basic-sdk/extension/plugins/table/plugin.js +20 -3
  10. package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/index.css +26 -9
  11. package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/rebase-delete-modify-decorate.js +9 -9
  12. package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/rebase-modify-delete-decorate.js +8 -8
  13. package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/rebase-modify-modify-decorate.js +8 -11
  14. package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +8 -0
  15. package/dist/basic-sdk/utils/rebase.js +20 -4
  16. package/dist/components/doc-operations/index.js +1 -2
  17. package/dist/components/doc-operations/more-operations.js +17 -4
  18. package/dist/components/tip-dialog/index.js +1 -1
  19. package/package.json +1 -1
  20. package/public/locales/en/sdoc-editor.json +4 -1
  21. package/public/locales/fr/sdoc-editor.json +2 -1
  22. package/public/locales/ru/sdoc-editor.json +2 -2
  23. package/public/locales/zh_CN/sdoc-editor.json +5 -2
  24. package/dist/components/doc-operations/history-operation.js +0 -23
@@ -33,11 +33,13 @@ class SeafileAPI {
33
33
  const url = '/api/v2.1/seadoc/upload-image/' + docUuid + '/';
34
34
  const form = new FormData();
35
35
  for (const fileItem of imageFiles) {
36
- const fileName = this.getImageFileNameWithTimestamp(fileItem);
37
- const file = new File([fileItem], fileName, {
38
- type: fileItem.type
39
- });
40
- form.append('file', file);
36
+ if (fileItem.type.startsWith('image/')) {
37
+ const fileName = this.getImageFileNameWithTimestamp(fileItem);
38
+ const file = new File([fileItem], fileName, {
39
+ type: fileItem.type
40
+ });
41
+ form.append('file', file);
42
+ }
41
43
  }
42
44
  return this.req.post(url, form);
43
45
  }
@@ -29,9 +29,9 @@ export const MODIFY_TYPE = {
29
29
  export const REBASE_TYPE = {
30
30
  MODIFY_MODIFY: 'modify_modify',
31
31
  DELETE_MODIFY: 'delete_modify',
32
- MODIFY_DELETE: 'modify_delete',
33
- CHILDREN_MODIFY: 'children_modify'
32
+ MODIFY_DELETE: 'modify_delete'
34
33
  };
34
+ export const REBASE_TYPES = [REBASE_TYPE.MODIFY_DELETE, REBASE_TYPE.DELETE_MODIFY, REBASE_TYPE.MODIFY_MODIFY];
35
35
  export const REBASE_MARK_KEY = {
36
36
  ORIGIN: 'origin',
37
37
  REBASE_TYPE: 'rebase_type',
@@ -1,14 +1,10 @@
1
- import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
- import { Editor, Element, Point, Transforms, Node, Range } from '@seafile/slate';
3
- import { ReactEditor } from '@seafile/slate-react';
4
- import { generateEmptyElement, getSelectedNodeByType, isSelectionAtBlockStart } from '../../core';
1
+ import { Editor, Element, Transforms, Node } from '@seafile/slate';
2
+ import { focusEditor, generateDefaultText, getSelectedNodeByType, isSelectionAtBlockStart } from '../../core';
5
3
  import { BLOCKQUOTE, PARAGRAPH, CODE_BLOCK, TABLE } from '../../constants';
6
4
  import { setBlockQuoteType, getFormattedElements, getFormattedRestElements } from './helpers';
7
- import { getCalloutEntry, insertElementAtNewLineInCallout } from '../callout/helper';
8
5
  const withBlockquote = editor => {
9
6
  const {
10
7
  insertBreak,
11
- insertText,
12
8
  deleteBackward,
13
9
  insertFragment
14
10
  } = editor;
@@ -18,40 +14,38 @@ const withBlockquote = editor => {
18
14
  selection
19
15
  } = editor;
20
16
  if (selection == null) return insertBreak();
21
- const [nodeEntry] = Editor.nodes(editor, {
17
+ const [quoteBlockEntry] = Editor.nodes(editor, {
22
18
  match: n => Element.isElement(n) && n.type === BLOCKQUOTE,
23
19
  universal: true
24
20
  });
25
- if (!nodeEntry) return insertBreak();
26
- const quoteElem = nodeEntry[0];
27
- const quotePath = ReactEditor.findPath(editor, quoteElem);
28
- const quoteEndLocation = Editor.end(editor, quotePath);
29
- if (Point.equals(quoteEndLocation, selection.focus)) {
30
- // Cursor is at the end of blockquote
31
- const str = Node.string(quoteElem);
32
- // The last of the blockquote text is \n;
33
- if (str && str.slice(-1) === '\n') {
34
- const calloutEntry = getCalloutEntry(editor);
35
- if (calloutEntry) {
36
- // If the cursor is at the end of the callout, insert a paragraph inside the callout
37
- // Remove the last \n
38
- editor.deleteBackward('character');
39
- insertElementAtNewLineInCallout(editor, PARAGRAPH, quotePath);
40
- return;
41
- }
42
- // Step 1: Remove the last \n
43
- editor.deleteBackward('character');
44
- // Step 2: Insert a paragraph
45
- const p = generateEmptyElement(PARAGRAPH);
46
- Transforms.insertNodes(newEditor, p, {
47
- mode: 'highest'
21
+ if (!quoteBlockEntry) return insertBreak();
22
+ const [currentLineEntry] = Editor.nodes(newEditor, {
23
+ match: n => Element.isElement(n) && n.type === PARAGRAPH,
24
+ mode: 'lowest'
25
+ });
26
+ // Exit blockquote when current line is empty
27
+ const isAtEnd = currentLineEntry[1].slice(-1)[0] === quoteBlockEntry[0].children.length - 1;
28
+ if (isAtEnd) {
29
+ const isEmptyLine = !(currentLineEntry && Editor.string(newEditor, currentLineEntry[1]).length);
30
+ const movePath = quoteBlockEntry[1];
31
+ movePath.push(movePath.pop() + 1);
32
+ if (isEmptyLine) {
33
+ Transforms.moveNodes(newEditor, {
34
+ at: currentLineEntry[1],
35
+ to: movePath
48
36
  });
49
37
  return;
50
38
  }
51
39
  }
52
40
 
53
- // In other cases, insert a newline
54
- insertText('\n');
41
+ // Insert new line
42
+ Transforms.insertNodes(editor, {
43
+ type: PARAGRAPH,
44
+ children: [generateDefaultText()]
45
+ }, {
46
+ at: newEditor.selection,
47
+ select: true
48
+ });
55
49
  };
56
50
  newEditor.deleteBackward = unit => {
57
51
  const {
@@ -63,32 +57,30 @@ const withBlockquote = editor => {
63
57
  }
64
58
  const node = getSelectedNodeByType(editor, BLOCKQUOTE);
65
59
  if (node) {
66
- if (isSelectionAtBlockStart(editor)) {
60
+ const [currentLineEntry] = Editor.nodes(newEditor, {
61
+ match: n => Element.isElement(n) && n.type === PARAGRAPH,
62
+ mode: 'lowest'
63
+ });
64
+ if (!currentLineEntry) return deleteBackward(unit);
65
+ const currentLineIndex = currentLineEntry[1].slice(-1)[0];
66
+ if (currentLineIndex === 0) {
67
67
  setBlockQuoteType(editor, PARAGRAPH);
68
68
  return;
69
69
  }
70
- if (unit === 'line' && Range.isCollapsed(selection)) {
71
- const quoteString = Node.string(node);
72
- for (let index = selection.anchor.offset; index > 1; index--) {
73
- const char = quoteString.slice(index - 1, index);
74
- if (char === '\n') {
75
- const deleteRange = selection.anchor.offset === index ? _objectSpread(_objectSpread({}, selection), {}, {
76
- anchor: _objectSpread(_objectSpread({}, selection.anchor), {}, {
77
- offset: index - 1
78
- })
79
- }) // cursor at beginning of line, delete \n
80
- : _objectSpread(_objectSpread({}, selection), {}, {
81
- anchor: _objectSpread(_objectSpread({}, selection.anchor), {}, {
82
- offset: index
83
- })
84
- }); // cursor at middle or end of line, save \n
85
- Transforms.delete(editor, {
86
- at: deleteRange
87
- });
88
- return;
89
- }
90
- }
91
- deleteBackward(unit);
70
+ if (isSelectionAtBlockStart(editor)) {
71
+ const lineText = Node.string(currentLineEntry[0]);
72
+ const previousNodeEntry = Editor.previous(editor, {
73
+ at: currentLineEntry[1]
74
+ });
75
+ const focusPoint = Editor.end(newEditor, previousNodeEntry[1]);
76
+ Transforms.insertText(newEditor, lineText, {
77
+ at: Editor.end(newEditor, previousNodeEntry[1])
78
+ });
79
+ Transforms.removeNodes(editor, {
80
+ at: currentLineEntry[1]
81
+ });
82
+ focusEditor(newEditor, focusPoint);
83
+ return;
92
84
  }
93
85
  }
94
86
  deleteBackward(unit);
@@ -141,4 +141,9 @@ export const isSingleImage = data => {
141
141
  if (Node.string(data[0]).length !== 0) return false;
142
142
  if (data[0].children.filter(item => (item === null || item === void 0 ? void 0 : item.type) === IMAGE).length !== 1) return false;
143
143
  return true;
144
+ };
145
+ export const insertImageFiles = (files, editor, targetPath) => {
146
+ context.uploadLocalImage(files).then(fileUrl => {
147
+ insertImage(editor, fileUrl, targetPath, INSERT_POSITION.AFTER);
148
+ });
144
149
  };
@@ -19,7 +19,6 @@ const ImageHoverMenu = _ref => {
19
19
  element,
20
20
  parentNodeEntry,
21
21
  onHideImageHoverMenu,
22
- onShowCaption,
23
22
  t
24
23
  } = _ref;
25
24
  const {
@@ -31,7 +30,7 @@ const ImageHoverMenu = _ref => {
31
30
  type
32
31
  } = parentNodeEntry[0];
33
32
  const {
34
- caption = ''
33
+ show_caption = false
35
34
  } = data;
36
35
  const [popoverState, setPopoverState] = useState({
37
36
  displayPopover: false,
@@ -125,6 +124,14 @@ const ImageHoverMenu = _ref => {
125
124
  onHideImageHoverMenu();
126
125
  return;
127
126
  }
127
+ if (Object.keys(props)[0] === 'show_caption') {
128
+ Transforms.setNodes(editor, {
129
+ data: _objectSpread(_objectSpread({}, data), props)
130
+ }, {
131
+ at: path
132
+ });
133
+ return;
134
+ }
128
135
  }
129
136
  // eslint-disable-next-line react-hooks/exhaustive-deps
130
137
  }, []);
@@ -182,11 +189,11 @@ const ImageHoverMenu = _ref => {
182
189
  id: "sdoc_image_caption",
183
190
  role: "button",
184
191
  className: classnames('op-item', 'ml-1', {
185
- 'active': (caption === null || caption === void 0 ? void 0 : caption.length) !== 0
192
+ 'active': show_caption
186
193
  }),
187
- onClick: () => {
188
- onShowCaption();
189
- }
194
+ onClick: event => onSelect(event, {
195
+ 'show_caption': !show_caption
196
+ })
190
197
  }, /*#__PURE__*/React.createElement("i", {
191
198
  className: "sdocfont sdoc-describe icon-font mr-1"
192
199
  }), isShowTooltip && /*#__PURE__*/React.createElement(Tooltip, {
@@ -10,7 +10,7 @@ import { INTERNAL_EVENT } from '../../../constants';
10
10
  import ImageHoverMenu from './hover-menu';
11
11
  import { useScrollContext } from '../../../hooks/use-scroll-context';
12
12
  import { IMAGE_BORDER_TYPE } from './constants';
13
- import { ADDED_STYLE, DELETED_STYLE } from '../../constants';
13
+ import { ADDED_STYLE, DELETED_STYLE, IMAGE_BLOCK } from '../../constants';
14
14
  import imagePlaceholder from '../../../assets/images/image-placeholder.png';
15
15
  const Image = _ref => {
16
16
  let {
@@ -27,6 +27,9 @@ const Image = _ref => {
27
27
  data,
28
28
  border_type = IMAGE_BORDER_TYPE[0].type
29
29
  } = element;
30
+ const {
31
+ show_caption = false
32
+ } = data;
30
33
  const path = ReactEditor.findPath(editor, element);
31
34
  const nodeEntry = Editor.node(editor, [path[0]]);
32
35
  const imageStyle = {
@@ -42,7 +45,6 @@ const Image = _ref => {
42
45
  const [isShowImageHoverMenu, setIsShowImageHoverMenu] = useState(false);
43
46
  const [menuPosition, setMenuPosition] = useState({});
44
47
  const [caption, setCaption] = useState((data === null || data === void 0 ? void 0 : data.caption) || '');
45
- const [isShowCaption, setIsShowCaption] = useState(false);
46
48
  const registerEvent = useCallback(eventList => {
47
49
  eventList.forEach(element => {
48
50
  document.addEventListener(element.eventName, element.event);
@@ -114,7 +116,8 @@ const Image = _ref => {
114
116
  var _imagePreviewer$;
115
117
  if (isResizing) return;
116
118
  const imagePreviewer = document.getElementsByClassName('sf-editor-image-previewer');
117
- if (e.target === imageRef.current || ((_imagePreviewer$ = imagePreviewer[0]) === null || _imagePreviewer$ === void 0 ? void 0 : _imagePreviewer$.contains(e.target))) return;
119
+ const isCaptionInput = e.target.id === 'sdoc-image-caption-input';
120
+ if (e.target === imageRef.current || ((_imagePreviewer$ = imagePreviewer[0]) === null || _imagePreviewer$ === void 0 ? void 0 : _imagePreviewer$.contains(e.target)) || isCaptionInput) return;
118
121
  setIsShowImageHoverMenu(false);
119
122
  }, [isResizing]);
120
123
  useEffect(() => {
@@ -225,7 +228,8 @@ const Image = _ref => {
225
228
  }), isResizing && /*#__PURE__*/React.createElement("span", {
226
229
  className: "image-size",
227
230
  contentEditable: false
228
- }, /*#__PURE__*/React.createElement("span", null, t('Width'), ':', parseInt(movingWidth || imageRef.current.clientWidth)), /*#__PURE__*/React.createElement("span", null, "\xA0\xA0"), /*#__PURE__*/React.createElement("span", null, t('Height'), ':', imageRef.current.clientHeight))), nodeEntry[0].type === 'image_block' && (isShowCaption || (data === null || data === void 0 ? void 0 : data.caption)) && /*#__PURE__*/React.createElement("input", {
231
+ }, /*#__PURE__*/React.createElement("span", null, t('Width'), ':', parseInt(movingWidth || imageRef.current.clientWidth)), /*#__PURE__*/React.createElement("span", null, "\xA0\xA0"), /*#__PURE__*/React.createElement("span", null, t('Height'), ':', imageRef.current.clientHeight))), nodeEntry[0].type === IMAGE_BLOCK && show_caption && /*#__PURE__*/React.createElement("input", {
232
+ id: "sdoc-image-caption-input",
229
233
  className: "sdoc-image-caption-input-wrapper",
230
234
  style: {
231
235
  width: (data === null || data === void 0 ? void 0 : data.width) || imageRef.current.clientWidth
@@ -246,9 +250,6 @@ const Image = _ref => {
246
250
  parentNodeEntry: nodeEntry,
247
251
  onHideImageHoverMenu: () => {
248
252
  setIsShowImageHoverMenu(false);
249
- },
250
- onShowCaption: () => {
251
- setIsShowCaption(true);
252
253
  }
253
254
  })));
254
255
  };
@@ -151,10 +151,16 @@ export const getBeforeText = editor => {
151
151
  };
152
152
  };
153
153
  export const isTriggeredByShortCut = editor => {
154
+ const {
155
+ selection
156
+ } = editor;
157
+ const {
158
+ anchor
159
+ } = selection;
154
160
  const {
155
161
  beforeText
156
162
  } = getBeforeText(editor);
157
- return beforeText.slice(-2) === '[[';
163
+ return beforeText.slice(-1) === '[' && Editor.isEnd(editor, anchor, anchor.path);
158
164
  };
159
165
 
160
166
  // If insert operation is triggered by shortcut, remove the '[[' symbol
@@ -167,12 +173,12 @@ export const removeShortCutSymbol = editor => {
167
173
  beforeText,
168
174
  range: beforeRange
169
175
  } = getBeforeText(editor);
170
- const isTrrigeredByShortCut = beforeText.slice(-3) === '[[ ';
176
+ const isTrrigeredByShortCut = beforeText.slice(-2) === '[[';
171
177
  isTrrigeredByShortCut && Transforms.delete(editor, {
172
178
  at: {
173
179
  anchor: {
174
180
  path: beforeRange.focus.path,
175
- offset: beforeText.length - 3
181
+ offset: beforeText.length - 2
176
182
  },
177
183
  focus: _objectSpread({}, selection.focus)
178
184
  },
@@ -49,8 +49,8 @@ const withSdocLink = editor => {
49
49
  const {
50
50
  key
51
51
  } = event;
52
- if (key !== ' ') return onHotKeyDown && onHotKeyDown(event);
53
- // If user press whitespace after '[[', open file modal
52
+ if (key !== '[') return onHotKeyDown && onHotKeyDown(event);
53
+ // If user press '[[', open file modal
54
54
  const eventBus = EventBus.getInstance();
55
55
  if (isTriggeredByShortCut(newEditor)) {
56
56
  eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
@@ -1,8 +1,9 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import isHotkey from 'is-hotkey';
3
- import { Editor, Transforms, Path } from '@seafile/slate';
4
- import { getNodeType, getParentNode, getSelectedNodeByType, isLastNode, generateEmptyElement } from '../../core';
5
- import { ELEMENT_TYPE, KEYBOARD, PARAGRAPH, CLIPBOARD_FORMAT_KEY } from '../../constants';
3
+ import { Editor, Transforms, Path, Element } from '@seafile/slate';
4
+ import { ReactEditor } from '@seafile/slate-react';
5
+ import { getNodeType, getParentNode, getSelectedNodeByType, isLastNode, generateEmptyElement, focusEditor } from '../../core';
6
+ import { ELEMENT_TYPE, KEYBOARD, PARAGRAPH, CLIPBOARD_FORMAT_KEY, CHECK_LIST_ITEM, ORDERED_LIST, UNORDERED_LIST } from '../../constants';
6
7
  import { TABLE_MAX_ROWS, EMPTY_SELECTED_RANGE, TABLE_ELEMENT, TABLE_ELEMENT_POSITION } from './constants';
7
8
  import ObjectUtils from '../../../utils/object-utils';
8
9
  import { getSelectedInfo, insertTableElement, removeTable, insertMultipleRowsAndColumns, setTableFragmentData, deleteTableRangeData, focusCell, deleteHandler, isTableLocation, isLastTableCell } from './helpers';
@@ -88,9 +89,25 @@ const withTable = editor => {
88
89
  const before = Editor.before(newEditor, selection);
89
90
  if (before) {
90
91
  // If the current is not a table and the previous one is a table, no deletion will be performed. Otherwise, the last cell of the table will be deleted
92
+ // If deleting node is empty check-list, order-list, unordered-list, change to paragraph
91
93
  const isTableOnBeforeLocation = isTableLocation(newEditor, before);
92
94
  const isTableOnCurSelection = isTableLocation(newEditor, selection);
93
95
  if (isTableOnBeforeLocation && !isTableOnCurSelection) {
96
+ const transformTypes = [CHECK_LIST_ITEM, ORDERED_LIST, UNORDERED_LIST];
97
+ const [currentNodeEntry] = Editor.nodes(editor, {
98
+ match: n => Element.isElement(n) && !Editor.parent(n, ReactEditor.findPath(editor, n))[1].length
99
+ });
100
+ if (!currentNodeEntry) return;
101
+ const [currentNode, currentPath] = Array.from(currentNodeEntry);
102
+ if (transformTypes.includes(currentNode.type)) {
103
+ Transforms.delete(newEditor, {
104
+ at: currentPath
105
+ });
106
+ Transforms.insertNodes(newEditor, generateEmptyElement(PARAGRAPH), {
107
+ at: currentPath
108
+ });
109
+ focusEditor(editor, Editor.start(editor, currentPath));
110
+ }
94
111
  return;
95
112
  }
96
113
  }
@@ -1,29 +1,46 @@
1
1
  .sdoc-rebase-btn-group {
2
+ display: flex;
3
+ width: 100%;
2
4
  color: #aaa;
5
+ margin-bottom: 3px;
3
6
  }
4
7
 
5
8
  .sdoc-rebase-btn-group .sdoc-rebase-btn {
6
9
  cursor: pointer;
7
10
  }
8
11
 
9
- .sdoc-rebase-current-changes-start {
12
+ .sdoc-rebase-other-changes-title {
13
+ width: 100%;
14
+ padding-left: 4px;
10
15
  background-color: rgb(202, 232, 254);
16
+ border-radius: 3px 3px 0 0;
11
17
  }
12
18
 
13
- .sdoc-rebase-current-changes {
19
+ .sdoc-rebase-other-changes {
14
20
  background-color: rgba(202, 232, 254, .8);
21
+ border-radius: 0 0 3px 3px;
22
+ margin-bottom: 3px;
15
23
  }
16
24
 
17
- .sdoc-rebase-incoming-changes {
25
+ .sdoc-rebase-my-changes-title {
26
+ width: 100%;
27
+ padding-left: 4px;
28
+ background-color: rgb(212, 212, 254);
29
+ border-radius: 3px 3px 0 0;
30
+ }
31
+
32
+ .sdoc-rebase-my-changes {
18
33
  background-color: rgb(222, 232, 254);
34
+ border-radius: 0 0 3px 3px;
19
35
  }
20
36
 
21
- .sdoc-rebase-incoming-changes > *:first-child,
22
- .sdoc-rebase-current-changes > *:first-child {
23
- margin: 0;
24
- padding: 0.8em 0 0.8em 0.2em;
37
+ .sdoc-rebase-my-changes.empty,
38
+ .sdoc-rebase-other-changes.empty {
39
+ height: 3px;
25
40
  }
26
41
 
27
- .sdoc-rebase-incoming-changes-end {
28
- background-color: rgb(212, 212, 254);
42
+ .sdoc-rebase-my-changes > *:first-child,
43
+ .sdoc-rebase-other-changes > *:first-child {
44
+ margin: 0;
45
+ padding: 0.8em 0 0.8em 0.2em;
29
46
  }
@@ -35,7 +35,7 @@ const RebaseDeleteModifyDecorate = _ref => {
35
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
36
36
  }, [editor, element]);
37
37
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
38
- className: "w-100 d-flex sdoc-rebase-btn-group",
38
+ className: "sdoc-rebase-btn-group",
39
39
  contentEditable: false
40
40
  }, /*#__PURE__*/React.createElement("div", {
41
41
  className: "sdoc-rebase-btn",
@@ -51,17 +51,17 @@ const RebaseDeleteModifyDecorate = _ref => {
51
51
  className: "sdoc-rebase-btn",
52
52
  onClick: deleteMark
53
53
  }, t('Keep_both_modification'))), /*#__PURE__*/React.createElement("div", {
54
- className: "w-100 sdoc-rebase-current-changes-start",
54
+ className: "sdoc-rebase-other-changes-title",
55
55
  contentEditable: false
56
- }, '<<<<<<<'), /*#__PURE__*/React.createElement("div", {
57
- className: "w-100",
56
+ }, t('Other_modification')), /*#__PURE__*/React.createElement("div", {
57
+ className: "sdoc-rebase-other-changes empty",
58
58
  contentEditable: false
59
- }, '======='), /*#__PURE__*/React.createElement("div", {
60
- className: "w-100 sdoc-rebase-incoming-changes",
59
+ }), /*#__PURE__*/React.createElement("div", {
60
+ className: "sdoc-rebase-my-changes-title",
61
61
  contentEditable: false
62
- }, children), /*#__PURE__*/React.createElement("div", {
63
- className: "w-100 sdoc-rebase-incoming-changes-end",
62
+ }, t('My_modification')), /*#__PURE__*/React.createElement("div", {
63
+ className: "sdoc-rebase-my-changes",
64
64
  contentEditable: false
65
- }, '>>>>>>>'));
65
+ }, children));
66
66
  };
67
67
  export default RebaseDeleteModifyDecorate;
@@ -35,7 +35,7 @@ const RebaseModifyDeleteDecorate = _ref => {
35
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
36
36
  }, [editor, element]);
37
37
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
38
- className: "w-100 d-flex sdoc-rebase-btn-group",
38
+ className: "sdoc-rebase-btn-group",
39
39
  contentEditable: false
40
40
  }, /*#__PURE__*/React.createElement("div", {
41
41
  className: "sdoc-rebase-btn",
@@ -51,17 +51,17 @@ const RebaseModifyDeleteDecorate = _ref => {
51
51
  className: "sdoc-rebase-btn",
52
52
  onClick: deleteMark
53
53
  }, t('Keep_both_modification'))), /*#__PURE__*/React.createElement("div", {
54
- className: "w-100 sdoc-rebase-current-changes-start",
54
+ className: "sdoc-rebase-other-changes-title",
55
55
  contentEditable: false
56
- }, '<<<<<<<'), /*#__PURE__*/React.createElement("div", {
57
- className: "w-100 sdoc-rebase-incoming-changes",
56
+ }, t('Other_modification')), /*#__PURE__*/React.createElement("div", {
57
+ className: "w-100 sdoc-rebase-my-changes",
58
58
  contentEditable: false
59
59
  }, children), /*#__PURE__*/React.createElement("div", {
60
- className: "w-100",
60
+ className: "sdoc-rebase-my-changes-title",
61
61
  contentEditable: false
62
- }, '======='), /*#__PURE__*/React.createElement("div", {
63
- className: "w-100 sdoc-rebase-incoming-changes-end",
62
+ }, t('My_modification')), /*#__PURE__*/React.createElement("div", {
63
+ className: "sdoc-rebase-my-changes empty",
64
64
  contentEditable: false
65
- }, '>>>>>>>'));
65
+ }));
66
66
  };
67
67
  export default RebaseModifyDeleteDecorate;
@@ -62,7 +62,7 @@ const RebaseModifyModifyDecorate = _ref => {
62
62
  }, [editor, element]);
63
63
  if (element[REBASE_MARK_KEY.ORIGIN] === REBASE_ORIGIN.OTHER) {
64
64
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
65
- className: "w-100 d-flex sdoc-rebase-btn-group",
65
+ className: "sdoc-rebase-btn-group",
66
66
  contentEditable: false
67
67
  }, /*#__PURE__*/React.createElement("div", {
68
68
  className: "sdoc-rebase-btn",
@@ -78,22 +78,19 @@ const RebaseModifyModifyDecorate = _ref => {
78
78
  className: "sdoc-rebase-btn",
79
79
  onClick: useBothChanges
80
80
  }, t('Keep_both_modification'))), /*#__PURE__*/React.createElement("div", {
81
- className: "w-100 sdoc-rebase-current-changes-start",
81
+ className: "sdoc-rebase-other-changes-title",
82
82
  contentEditable: false
83
- }, '<<<<<<<'), /*#__PURE__*/React.createElement("div", {
84
- className: "w-100 sdoc-rebase-current-changes",
83
+ }, t('Other_modification')), /*#__PURE__*/React.createElement("div", {
84
+ className: "sdoc-rebase-other-changes",
85
85
  contentEditable: false
86
86
  }, children));
87
87
  }
88
88
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
89
- className: "w-100",
89
+ className: "sdoc-rebase-my-changes-title",
90
90
  contentEditable: false
91
- }, '======='), /*#__PURE__*/React.createElement("div", {
92
- className: "w-100 sdoc-rebase-incoming-changes",
91
+ }, t('My_modification')), /*#__PURE__*/React.createElement("div", {
92
+ className: "sdoc-rebase-my-changes",
93
93
  contentEditable: false
94
- }, children), /*#__PURE__*/React.createElement("div", {
95
- className: "w-100 sdoc-rebase-incoming-changes-end",
96
- contentEditable: false
97
- }, '>>>>>>>'));
94
+ }, children));
98
95
  };
99
96
  export default RebaseModifyModifyDecorate;
@@ -7,6 +7,7 @@ import EventBus from '../../../utils/event-bus';
7
7
  import { useScrollContext } from '../../../hooks/use-scroll-context';
8
8
  import { focusEditor } from '../../core';
9
9
  import { getDomTopHeight, setSelection, isVoidNode, getNodeEntry, isBlockquote, isList, onWrapListItem } from './helpers';
10
+ import { insertImageFiles } from '../../plugins/image/helpers';
10
11
  import { INTERNAL_EVENT } from '../../../constants';
11
12
  import { CODE_BLOCK, TABLE, BLOCKQUOTE, CHECK_LIST_ITEM, CALL_OUT } from '../../constants';
12
13
  import { getCalloutEntry } from '../../plugins/callout/helper';
@@ -106,6 +107,13 @@ const SideToolbar = () => {
106
107
  const drop = useCallback(event => {
107
108
  targetElement = event.currentTarget;
108
109
  targetElement.classList.remove('sdoc-draging');
110
+
111
+ // Drag local image files to sdoc
112
+ if (event.dataTransfer.files.length > 0) {
113
+ const [, targetPath] = getNodeEntry(editor, targetElement);
114
+ insertImageFiles(event.dataTransfer.files, editor, targetPath);
115
+ return;
116
+ }
109
117
  const [sourceNode, sourcePath] = getNodeEntry(editor, sourceElement);
110
118
  const [, targetPath] = getNodeEntry(editor, targetElement);
111
119
 
@@ -1,12 +1,29 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import { generateIdMapAndIds, getIdDiffs } from './diff';
3
3
  import ObjectUtils from './object-utils';
4
- import { MODIFY_TYPE, REBASE_TYPE, REBASE_MARK_KEY, REBASE_ORIGIN } from '../constants';
4
+ import { MODIFY_TYPE, REBASE_TYPE, REBASE_MARK_KEY, REBASE_ORIGIN, REBASE_TYPES } from '../constants';
5
5
  import { ELEMENT_TYPE } from '../extension/constants';
6
6
  import { replaceNodeId } from '../node-id/helpers';
7
7
  export const hasConflict = content => {
8
8
  if (!Array.isArray(content) || content.length === 0) return false;
9
- return content.filter(item => item.rebase_type).length !== 0;
9
+ let flag = false;
10
+ for (let i = 0; i < content.length; i++) {
11
+ const element = content[i];
12
+ const {
13
+ rebase_type,
14
+ children
15
+ } = element;
16
+ if (REBASE_TYPES.includes(rebase_type)) {
17
+ flag = true;
18
+ break;
19
+ } else {
20
+ const childrenFlag = hasConflict(children);
21
+ if (!childrenFlag) continue;
22
+ flag = childrenFlag;
23
+ break;
24
+ }
25
+ }
26
+ return flag;
10
27
  };
11
28
  const getChanges = (masterContent, revisionContent) => {
12
29
  const {
@@ -156,8 +173,7 @@ const getMergeElement = (diffElement, baseElement) => {
156
173
  content: childrenContent
157
174
  } = getMergeContent(baseElement, diffElement.children);
158
175
  return [_objectSpread(_objectSpread({}, newElement), {}, {
159
- children: childrenContent,
160
- [REBASE_MARK_KEY.REBASE_TYPE]: REBASE_TYPE.CHILDREN_MODIFY
176
+ children: childrenContent
161
177
  })];
162
178
  }
163
179
  newElement[REBASE_MARK_KEY.OLD_ELEMENT] && delete newElement[REBASE_MARK_KEY.OLD_ELEMENT];
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import { withTranslation } from 'react-i18next';
3
3
  import context from '../../context';
4
4
  import RevisionOperations from './revision-operations';
5
- import HistoryOperation from './history-operation';
6
5
  import CollaboratorsOperation from './collaborators-operation';
7
6
  import MoreOperations from './more-operations';
8
7
  import CommentsOperation from './comments-operation';
@@ -34,6 +33,6 @@ const DocOperations = _ref => {
34
33
  handleViewChangesToggle: handleViewChangesToggle,
35
34
  handleRevisionMerged: handleRevisionMerged,
36
35
  handleRevisionPublished: handleRevisionPublished
37
- }), !isPublished && /*#__PURE__*/React.createElement(TagOperation, null), !isPublished && /*#__PURE__*/React.createElement(CommentsOperation, null), !isSdocRevision && /*#__PURE__*/React.createElement(ShareOperation, null), /*#__PURE__*/React.createElement(HistoryOperation, null), !isPublished && /*#__PURE__*/React.createElement(CollaboratorsOperation, null), !isSdocRevision && /*#__PURE__*/React.createElement(MoreOperations, null));
36
+ }), !isPublished && /*#__PURE__*/React.createElement(TagOperation, null), !isPublished && /*#__PURE__*/React.createElement(CommentsOperation, null), !isSdocRevision && /*#__PURE__*/React.createElement(ShareOperation, null), !isPublished && /*#__PURE__*/React.createElement(CollaboratorsOperation, null), !isSdocRevision && /*#__PURE__*/React.createElement(MoreOperations, null));
38
37
  };
39
38
  export default withTranslation('sdoc-editor')(DocOperations);
@@ -9,6 +9,12 @@ const MoreOperations = _ref => {
9
9
  t
10
10
  } = _ref;
11
11
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
12
+ const parentFolderURL = context.getSetting('parentFolderURL');
13
+ const isPro = context.getSetting('isPro');
14
+ const isFreezed = context.getSetting('isFreezed');
15
+ const docPerm = context.getSetting('docPerm');
16
+ const historyURL = context.getSetting('historyURL');
17
+ const isSdocRevision = context.getSetting('isSdocRevision');
12
18
  const toggleDropdown = useCallback(isDropdownOpen => {
13
19
  setIsDropdownOpen(!isDropdownOpen);
14
20
  }, []);
@@ -20,9 +26,13 @@ const MoreOperations = _ref => {
20
26
  const eventBus = EventBus.getInstance();
21
27
  eventBus.dispatch(EXTERNAL_EVENT.UNFREEZE);
22
28
  }, []);
23
- const parentFolderURL = context.getSetting('parentFolderURL');
24
- const isPro = context.getSetting('isPro');
25
- const isFreezed = context.getSetting('isFreezed');
29
+ const handleClickHistory = useCallback(event => {
30
+ if (docPerm !== 'rw' || !historyURL) return;
31
+ if (isSdocRevision) return;
32
+ event.stopPropagation();
33
+ event.nativeEvent.stopImmediatePropagation();
34
+ window.location.href = historyURL;
35
+ }, [docPerm, historyURL, isSdocRevision]);
26
36
  return /*#__PURE__*/React.createElement(Dropdown, {
27
37
  isOpen: isDropdownOpen,
28
38
  toggle: () => toggleDropdown(isDropdownOpen)
@@ -44,6 +54,9 @@ const MoreOperations = _ref => {
44
54
  }, t('Unfreeze')), isPro && !isFreezed && /*#__PURE__*/React.createElement(DropdownItem, {
45
55
  className: "sdoc-dropdown-menu-item",
46
56
  onClick: onFreezeDocument
47
- }, t('Freeze_Document'))));
57
+ }, t('Freeze_Document')), /*#__PURE__*/React.createElement(DropdownItem, {
58
+ className: "sdoc-dropdown-menu-item",
59
+ onClick: handleClickHistory
60
+ }, t('Document_history'))));
48
61
  };
49
62
  export default withTranslation('sdoc-editor')(MoreOperations);
@@ -27,7 +27,7 @@ const TipDialog = _ref => {
27
27
  // eslint-disable-next-line react-hooks/exhaustive-deps
28
28
  }, [tipType, isSubmitting]);
29
29
  const onSubmit = useCallback(() => {
30
- if (tipType === TIP_TYPE.HAS_CONFLICT_BEFORE_VIEW_CHANGES) {
30
+ if ([TIP_TYPE.HAS_CONFLICT_BEFORE_VIEW_CHANGES, TIP_TYPE.HAS_CONFLICT_BEFORE_PUBLISH].includes(tipType)) {
31
31
  closeDialog();
32
32
  return;
33
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.3.29",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -425,5 +425,8 @@
425
425
  "Column_number": "Column number",
426
426
  "The_maximum_row_number_is_{number}": "The maximum row number is {number}",
427
427
  "Freeze_Document": "Freeze Document",
428
- "Unfreeze": "Unfreeze"
428
+ "Unfreeze": "Unfreeze",
429
+ "Other_modification": "Other's modification",
430
+ "My_modification": "My modification",
431
+ "Document_history": "Document history"
429
432
  }
@@ -424,5 +424,6 @@
424
424
  "Row_number": "Row number",
425
425
  "Column_number": "Column number",
426
426
  "The_maximum_row_number_is_{number}": "The maximum row number is {number}",
427
- "The_maximum_column_number_is_{number}": "The maximum column number is {number}"
427
+ "Freeze_Document": "Freeze Document",
428
+ "Unfreeze": "Unfreeze"
428
429
  }
@@ -424,6 +424,6 @@
424
424
  "Row_number": "Номер строки",
425
425
  "Column_number": "Номер столбца",
426
426
  "The_maximum_row_number_is_{number}": "Максимальное количество строк - {number}",
427
- "Freeze_Document": "Freeze Document",
428
- "Unfreeze": "Unfreeze"
427
+ "Freeze_Document": "Заморозить документ",
428
+ "Unfreeze": "Разморозить"
429
429
  }
@@ -376,7 +376,7 @@
376
376
  "Link_sdoc": "链接 sdoc",
377
377
  "Link_file": "文件链接",
378
378
  "Keep_my_modification": "保留我的更改",
379
- "Keep_other_modification": "保留其他的更改",
379
+ "Keep_other_modification": "保留他人的更改",
380
380
  "Keep_both_modification": "保留两者更改",
381
381
  "Tip": "提示",
382
382
  "Rebase_delete_no_change_revision_tip": "修订改没有更改,是否删除修订稿?",
@@ -425,5 +425,8 @@
425
425
  "Column_number": "列数",
426
426
  "The_maximum_row_number_is_{number}": "最大行数为 {number}",
427
427
  "Freeze_Document": "冻结文档",
428
- "Unfreeze": "取消冻结"
428
+ "Unfreeze": "取消冻结",
429
+ "Other_modification": "他人更改",
430
+ "My_modification": "我的更改",
431
+ "Document_history":"文档历史"
429
432
  }
@@ -1,23 +0,0 @@
1
- import React, { useCallback } from 'react';
2
- import context from '../../context';
3
- const HistoryOperation = () => {
4
- const docPerm = context.getSetting('docPerm');
5
- const historyURL = context.getSetting('historyURL');
6
- const isSdocRevision = context.getSetting('isSdocRevision');
7
- const toggleHistory = useCallback(event => {
8
- event.stopPropagation();
9
- event.nativeEvent.stopImmediatePropagation();
10
- window.location.href = historyURL;
11
-
12
- // eslint-disable-next-line react-hooks/exhaustive-deps
13
- }, []);
14
- if (docPerm !== 'rw' || !historyURL) return null;
15
- if (isSdocRevision) return null;
16
- return /*#__PURE__*/React.createElement("span", {
17
- className: "op-item",
18
- onClick: toggleHistory
19
- }, /*#__PURE__*/React.createElement("i", {
20
- className: "sdocfont sdoc-history"
21
- }));
22
- };
23
- export default HistoryOperation;