@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.
- package/dist/api/seafile-api.js +7 -5
- package/dist/basic-sdk/constants/index.js +2 -2
- package/dist/basic-sdk/extension/plugins/blockquote/plugin.js +47 -55
- package/dist/basic-sdk/extension/plugins/image/helpers.js +5 -0
- package/dist/basic-sdk/extension/plugins/image/hover-menu/index.js +13 -6
- package/dist/basic-sdk/extension/plugins/image/render-elem.js +8 -7
- package/dist/basic-sdk/extension/plugins/sdoc-link/helpers.js +9 -3
- package/dist/basic-sdk/extension/plugins/sdoc-link/plugin.js +2 -2
- package/dist/basic-sdk/extension/plugins/table/plugin.js +20 -3
- package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/index.css +26 -9
- package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/rebase-delete-modify-decorate.js +9 -9
- package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/rebase-modify-delete-decorate.js +8 -8
- package/dist/basic-sdk/extension/render/element-decorate/rebase-decorate/rebase-modify-modify-decorate.js +8 -11
- package/dist/basic-sdk/extension/toolbar/side-toolbar/index.js +8 -0
- package/dist/basic-sdk/utils/rebase.js +20 -4
- package/dist/components/doc-operations/index.js +1 -2
- package/dist/components/doc-operations/more-operations.js +17 -4
- package/dist/components/tip-dialog/index.js +1 -1
- package/package.json +1 -1
- package/public/locales/en/sdoc-editor.json +4 -1
- package/public/locales/fr/sdoc-editor.json +2 -1
- package/public/locales/ru/sdoc-editor.json +2 -2
- package/public/locales/zh_CN/sdoc-editor.json +5 -2
- package/dist/components/doc-operations/history-operation.js +0 -23
package/dist/api/seafile-api.js
CHANGED
|
@@ -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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
2
|
-
import {
|
|
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 [
|
|
17
|
+
const [quoteBlockEntry] = Editor.nodes(editor, {
|
|
22
18
|
match: n => Element.isElement(n) && n.type === BLOCKQUOTE,
|
|
23
19
|
universal: true
|
|
24
20
|
});
|
|
25
|
-
if (!
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
//
|
|
54
|
-
|
|
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
|
-
|
|
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 (
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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':
|
|
192
|
+
'active': show_caption
|
|
186
193
|
}),
|
|
187
|
-
onClick:
|
|
188
|
-
|
|
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
|
-
|
|
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 ===
|
|
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(-
|
|
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(-
|
|
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 -
|
|
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 !== '
|
|
53
|
-
// If user press
|
|
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 {
|
|
5
|
-
import {
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
22
|
-
.sdoc-rebase-
|
|
23
|
-
|
|
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-
|
|
28
|
-
|
|
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: "
|
|
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: "
|
|
54
|
+
className: "sdoc-rebase-other-changes-title",
|
|
55
55
|
contentEditable: false
|
|
56
|
-
}, '
|
|
57
|
-
className: "
|
|
56
|
+
}, t('Other_modification')), /*#__PURE__*/React.createElement("div", {
|
|
57
|
+
className: "sdoc-rebase-other-changes empty",
|
|
58
58
|
contentEditable: false
|
|
59
|
-
}
|
|
60
|
-
className: "
|
|
59
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
60
|
+
className: "sdoc-rebase-my-changes-title",
|
|
61
61
|
contentEditable: false
|
|
62
|
-
},
|
|
63
|
-
className: "
|
|
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: "
|
|
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: "
|
|
54
|
+
className: "sdoc-rebase-other-changes-title",
|
|
55
55
|
contentEditable: false
|
|
56
|
-
}, '
|
|
57
|
-
className: "w-100 sdoc-rebase-
|
|
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: "
|
|
60
|
+
className: "sdoc-rebase-my-changes-title",
|
|
61
61
|
contentEditable: false
|
|
62
|
-
}, '
|
|
63
|
-
className: "
|
|
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: "
|
|
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: "
|
|
81
|
+
className: "sdoc-rebase-other-changes-title",
|
|
82
82
|
contentEditable: false
|
|
83
|
-
}, '
|
|
84
|
-
className: "
|
|
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: "
|
|
89
|
+
className: "sdoc-rebase-my-changes-title",
|
|
90
90
|
contentEditable: false
|
|
91
|
-
}, '
|
|
92
|
-
className: "
|
|
91
|
+
}, t('My_modification')), /*#__PURE__*/React.createElement("div", {
|
|
92
|
+
className: "sdoc-rebase-my-changes",
|
|
93
93
|
contentEditable: false
|
|
94
|
-
}, children)
|
|
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
|
-
|
|
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),
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
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 (
|
|
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
|
@@ -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
|
-
"
|
|
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": "
|
|
428
|
-
"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;
|